One program that exercises every primer construct¶
Artifact — one small Go program that uses every construct in module 1.6's list, with a table-driven test that passes. The checklist requires it. It is the last item in the teaching order of the phase, and it has one purpose: it removes the language as a variable before P2 opens the k/k source.
Topology — none. This program builds on forge, which is up whatever topology runs. The cluster is already gone, because the incident note destroyed it.
Build
This program is not one of the eleven build-track artifacts, because the phase has none. No module path convention applies. No version stamp applies. No ship step from build-mechanics applies. The program is a few hundred lines in build/01-go-primer/.
Do not write six toy programs. Write one program that does something small and coherent. The constructs only teach you anything when they have to coexist. A worker pool that fetches N things with a deadline is enough, and it forces all six constructs:
build/01-go-primer/
go.mod
fetch.go the Fetcher interface and two implementations
pool.go the goroutines, the channel, the select, the WaitGroup
errors.go a typed error, wrapped and unwrapped
pool_test.go the table test
The program must do six things. You can prove each one by deleting it and watching something break.
- Take a
context.Contextas the first parameter of every function that can block. The caller sets a deadline, and the deadline cancels the context. Prove it: set the deadline shorter than the work, and confirm that the program returns early instead of finishing. - Run a producer that feeds a channel, a
selectwith actx.Done()case, and async.WaitGroupfor shutdown. Prove it: run the program undergo test -race. - Define an interface with two implementations, one real and one fake. The test uses the fake. Every
Interfacetype ink/kexists for this pattern. Using it here is what makes the fakes in P4 unremarkable. - Use struct embedding once, deliberately, at the one place where composition genuinely reads better than a field.
- Wrap your errors. Put a typed error at the bottom. Use
fmt.Errorf("...: %w", err)on the way up. At the top, useerrors.Isanderrors.As, each for the thing that it is for. Mixing the two up is the most common Go error-handling bug, and it is worth making that mistake once, on purpose. - Write one generic function. You need only enough to read them, because
client-gonow uses them. AMap[T, U]over a slice is plenty.
Verify
cd build/01-go-primer
go vet ./...
go test -race -run Test -v ./...
go test -run TestFetch -count=1 ./... # the table test, named cases in the output
Gate — three conditions must hold. go vet is clean. go test -race passes. The table test names each case in its output, through t.Run(tc.name, ...), instead of printing one pass line for the whole set. The named subtests are the shape, and not a nicety. Every test that you will read in k/k looks like this, and so will every test that you write against envtest in P4.
Expect — the race detector finds something on the first honest attempt. It is usually one of two things: a WaitGroup.Add inside the goroutine instead of before it, or the loop variable captured by the closure. Both are worth hitting once here, rather than in a controller. The other thing that usually needs a second pass is the distinction between errors.Is and errors.As. Is compares against a sentinel value. As extracts a typed error. %w is what makes either of them possible, through any number of layers of wrapping.
Write down — commit the program, with the output of go test -race -v. That transcript is the proof. The checklist asks for a passing table test, and this is how you show that it passed.
Teardown — run go clean -modcache on forge if disk is tight. build-mechanics#p5-split is the runbook, and it is the reason. No topology was involved, so there is nothing to destroy.