all_lessons / Go / index 18 lessons · ~7h read

Go from the Team Up

One linearized path from "why does this codebase keep getting harder to change?" to a small, idiomatic concurrent service you could defend in a design review. A first-principles track that teaches the transferable skill underneath the language: treating complexity as a payable cost — to the reader, to the team, over the lifetime of the code. The working language is Go; the engineering judgment is the point.

The thesis, in one sentence: where its companion track C++ from the Machine Up refuses to hide the machine, Go refuses to hide the maintenance bill — and was built, at Google scale, to solve software engineering rather than language theory. The series runs in five movements — the bet, composition without inheritance, concurrency from first principles, programming in the large, and the contract that assembles the whole. Read it straight through, or start at the orientation for the map.

Who this is for
You can read a little code in some language and know roughly what a pointer and a thread are; no Go and no systems background is assumed. By the end you can explain Go's value semantics and when data escapes to the heap; design with small interfaces and composition instead of inheritance; treat errors as values that live in the signature; reason about goroutines, channels, and when to reach for a mutex instead; structure packages and dependencies for a team; judge when a feature like generics earns its complexity; and walk the five-question habit over any design in any language.
How this relates to the C++ track
An original first-principles path designed as the deliberate counterpoint to C++ from the Machine Up. C++'s organizing principle is zero overhead — you don't pay for what you don't use. Go's is the bargain — complexity must be earned, and a small bounded runtime cost is worth paying for safety and velocity. Reading both builds the same trade-off literacy from opposite ends; neither track requires the other.

Part I · The bet (lessons 00–03)

What Go optimizes for, the problem that produced it, and the value-and-memory model everything later stands on.

00
Orientation — why Go makes you a better engineer
The thesis: Go was built to solve programming in the large, and treats simplicity as the tool. The bargain that organizes the language, the five-question habit it installs, and the 18-lesson dependency chain.
01
The problem Go was built to solve
Programming in the large: multi-minute builds, dependency tangles, and two-teams-two-dialects at Google scale — and how fast single-pass compilation, one static binary, gofmt, and a forbidden import cycle are each a direct response. Why omission is design, not deprivation.
02
Values, types, and the zero value
Go's value semantics: structs, arrays, slices (a 3-word header), and maps as data with a known layout; copies happen at the assignment and argument boundary; every type's zero value is usable. The memory model lessons 03–04 build on.
03
Pointers, the heap, escape analysis & the GC
Pointers without arithmetic; stack vs heap, decided by the compiler's escape analysis rather than by you; and the garbage collector as the runtime cost Go chose to pay — concurrent, sub-millisecond pauses, tunable with GOGC. The direct counterpoint to C++.

Part II · Composition — abstraction without inheritance (lessons 04–07)

How Go builds abstractions with no classes and no inheritance: behavior on values, dependence on behavior, assembly by embedding, and the payoff — errors as values.

04
Structs, methods, and receivers
Methods on any named type — no classes, just data plus functions with a receiver. Value vs pointer receiver as a direct consequence of value semantics: mutation, copy cost, and consistency decide which you pick. Method sets.
05
Interfaces
Depend on a behavior, not a type. Implicit (structural) satisfaction — no implements keyword; the interface value as a (type, data) pair; the typed-nil-in-interface trap; small interfaces; "accept interfaces, return structs."
06
Composition over inheritance
Struct and interface embedding, method promotion, and what you build instead of a class hierarchy. Why Go dropped implementation inheritance, the empty interface any, and type assertions and switches.
07
Errors are values
error is just a one-method interface, so failure is an ordinary returned value the caller must handle — visible in the signature. Wrapping with %w, errors.Is/As, sentinel vs typed errors, and why panic/recover are not exceptions.

Part III · Concurrency from first principles (lessons 08–11)

Go's headline feature, built on the values and interfaces of Part II: cheap workers, communication that moves ownership, composition and cancellation, and the cases where you share memory after all.

08
Goroutines and the scheduler
A goroutine vs an OS thread: a ~2 KB growable stack instead of ~1 MB, so a million is fine. The G-M-P scheduler, work-stealing, and preemption — and exactly what concurrency costs you in Go.
09
Channels and CSP
"Don't communicate by sharing memory; share memory by communicating." Channels as typed conduits that transfer ownership of a value; unbuffered (synchronization) vs buffered (decoupling); close semantics; the happens-before guarantee.
10
select, pipelines, and cancellation
Composing concurrent work: select, timeouts, fan-in/fan-out, and the pipeline pattern — then context.Context for propagating cancellation and deadlines, and how goroutines leak when you forget.
11
When to share memory
sync.Mutex/RWMutex/WaitGroup/Once and sync/atomic; the -race detector; the Go memory model in a paragraph. The rule of thumb: channels orchestrate work, mutexes protect state — and how to choose.

Part IV · Programming in the large (lessons 12–15)

The parts that make Go a team language: package boundaries and dependencies, a standard library that teaches taste, generics judged by the complexity budget, and tooling shipped with the language.

12
Packages, modules, and dependencies
The package as the unit of compilation and encapsulation (exported = capitalized); the acyclic import graph; modules, semantic import versioning, and minimal version selection. Designing boundaries, and "a little copying beats a little dependency."
13
The standard library as a teacher
io.Reader/io.Writer as the canonical small-interface composition — io.Copy works on anything that reads or writes. How net/http, encoding/json, and the stdlib are the idiom and teach Go taste.
14
Generics, and when they earn it
Type parameters and constraints (Go 1.18); the decade of deliberate resistance and what finally shifted the cost/benefit. Generics vs interfaces vs copy-paste — the complexity budget applied to a real, long-withheld feature.
15
Testing, benchmarks, and tooling as language
go test, table-driven tests, testing.B benchmarks, fuzzing, the race detector, and pprof — tooling shipped with the language, with uniformity enforced both mechanically (gofmt, vet) and socially.

Part V · The contract (lessons 16–17)

What "idiomatic" means and why readability is the metric — then the whole habit assembled into one service, with an honest accounting of what the bargain cost.

16
Idiomatic Go and the design philosophy
The Go proverbs that carry real weight, API design for readers, and why "idiomatic" is enforced by both gofmt and the community. Readability as the optimization target — the thing the whole bargain was protecting.
17
Capstone: a small concurrent service
Assemble interfaces, errors, goroutines, channels, context, packages, and tests into one worked design; walk the five questions over it; then name the honest costs of Go's bargain — GC tail latency, error-handling verbosity, a weaker type system, and the limits of simplicity.
How to read this
Straight through, in order — the series is a dependency chain, and each lesson's "Linear position" box names exactly what it assumes and what new capability it adds. If you only have time for the spine of the argument, read 00 → 02 → 05 → 07 → 09 → 17.