Day 15 — Gate II: Concurrency & Generics Checkpoint
Day 15 — Gate II: Concurrency & Generics Checkpoint
Day 38 — Stage IV gate
Stage IV · Gate day · ~3h
Goal: Prove generics + testing depth: ship a small package with a justified generic helper, serious tests (fuzz and/or goldens + concurrent/race), and a clean Go 1.26 quality bar.
Stage IV gate is not “more concurrency.” It is type-safe abstraction + proof. Weak tests fail the gate even if generics look clever.
Why this day exists
Stage IV (Days 31–37) stacked:
| Day | Skill |
|---|---|
| 31 | Type parameters, constraints, Map/Filter |
| 32 | Generic structs; 1.26 self-ref constraints |
| 33 | When not to use generics |
| 34 | Fuzzing |
| 35 | Golden files, Cleanup |
| 36 | Concurrent tests / synctest awareness |
| 37 | Go 1.26 quality (new, go fix, Green Tea GC notes) |
The gate merges judgment + evidence.
Theory 1 — Required artifact
One module (new or refined Stage IV package) that includes all of:
- Generic helper or type that earns its type parameter (Day 33 judgment)
- Table-driven tests for core logic
- At least one of:
Fuzztarget with seeds (+ any corpus crashers fixed)
- Golden-file test with update workflow documented
- Race-clean tests:
go test -race -count=10 ./...
- If the package is concurrent (optional but strong): cancel or limit test from Day 36
- README + short GATE.md (rubric self-score)
- Toolchain notes:
go 1.26/ 1.26.x features used (new(expr)or fix pass mentioned)
Examples of strong packages:
- Generic
Set[T comparable]+ fuzz on element serialization
- Config encoder with goldens + generic
Maphelpers
- Concurrent worker utility with generic job type + race tests + fuzz on job IDs
Avoid: generic-for-show wrappers around one concrete type.
Theory 2 — Rubric
| Criterion | Weight | 0 | 1 | 2 |
|---|---|---|---|---|
| Justified generics | 20% | none/abusive | ok | clearly earns keep |
| Test depth | 25% | thin | tables only | tables + fuzz/golden |
| Race hygiene | 15% | fails | occasional | -count=10 solid |
| Judgment writeup | 15% | missing | shallow | “why generic / why not more” |
| Go 1.26 awareness | 10% | none | notes | applied new/fix/mod line |
| README / UX | 10% | none | minimal | clear |
| Code clarity | 5% | messy | ok | tight |
Pass: ≥ 80%, no zero on justified generics or test depth.
Theory 3 — Judgment paragraph (required)
In GATE.md, answer:
- What is parameterized, and what is intentionally concrete?
- What would have to change for a second type parameter to be justified?
- Which tests would catch a regression if you refactored constraints?
Theory 4 — Quality bar commands
go version # 1.26.x
go fmt ./...
go vet ./...
go fix ./... # review diff
go test -race -count=10 ./...
go test -fuzz=FuzzX -fuzztime=20s # if fuzz presentAll must be documented; fuzz deep run can be local-only if CI timeboxed—seeds must pass in plain go test.
Theory 5 — Anti-patterns that fail the gate
Stack[T]with no tests
- Fuzz target that does nothing (
_ = in)
- Golden updated without review notes
- Generics only to avoid writing
inttwice
- Ignoring race failures
- Empty GATE.md
Theory 6 — Reference package skeleton (you may adapt)
day38/
go.mod # go 1.26
README.md
GATE.md
set/
set.go # Set[T comparable]
set_test.go # tables + parallel
set_fuzz_test.go # Fuzz round-trip via Format/Parse elements
# OR encode/
# encode.go
# encode_test.go # golden + tables
Minimal earning generic
package set
type Set[T comparable] struct {
m map[T]struct{}
}
func New[T comparable]() *Set[T] {
return &Set[T]{m: make(map[T]struct{})}
}
func (s *Set[T]) Add(v T) { s.m[v] = struct{}{} }
func (s *Set[T]) Has(v T) bool { _, ok := s.m[v]; return ok }
func (s *Set[T]) Len() int { return len(s.m) }
func (s *Set[T]) Elements() []T {
out := make([]T, 0, len(s.m))
for v := range s.m {
out = append(out, v)
}
return out
}Why this earns generics: one algorithm, many key types, map constraint is real (comparable). Why not more: no need for Set[T, U] fantasy parameters.
Fuzz angle (if elements are strings/ints)
Fuzz a ParseList → set → sorted elements → format round-trip, or fuzz Add/Has sequences with []int seeds. Keep the body panic-free and invariant-true.
Golden angle (alternative)
Pretty-print a deterministic Config (sorted tags) and compare to testdata/config.golden.json. Document UPDATE_GOLDEN=1 go test.
Theory 7 — Linking Stage III (optional boost)
If you add concurrent access to Set, you must either:
- Document not thread-safe (callers synchronize), or
- Provide
SyncSetwith mutex and-racetests
Do not pretend a generic type is concurrent-safe without locks. Gate bonus: concurrent test that fails without the mutex and passes with it under -race.
Lab 1 — Select and freeze scope
Workspace: ~/lab/90daysofx/01-go/day38 (or promote best of day31–37)
mkdir -p ~/lab/90daysofx/01-go/day38
cd ~/lab/90daysofx/01-go/day38
go mod init example.com/day38Write a 10-line design blurb before coding more:
- Package purpose
- Generic surface
- Test strategy (tables + fuzz or golden)
- Explicit non-goals
Lab 2 — Implement / harden
- Complete generic API with constructors and zero-value story
- Table-driven tests for edge cases (empty, duplicate add, missing has)
- Fuzz or golden as required
go test -race -count=10 ./...
- README with copy-paste commands
go fmt ./...
go vet ./...
go fix ./... # review
go test -race -count=10 ./...If fuzz exists:
go test -fuzz=FuzzSet -fuzztime=20sLab 3 — Self-score with evidence
Fill rubric table in GATE.md with paths:
| Criterion | Score | Evidence |
|-----------|-------|----------|
| Justified generics | 2 | set/set.go Set[T comparable] |
| Test depth | 2 | set_test.go + set_fuzz_test.go |
...Add the three judgment answers from Theory 3.
Lab 4 — Oral dry-run + regression drill
Explain in 3 minutes (bullet script):
- Why this is generic
- One invariant fuzz/golden enforces
- How
-raceis part of definition of done
- One Go 1.26 note you applied (
new(expr), Green Tea default awareness, orgo fix)
Then:
- Break one invariant on purpose
- Show test/fuzz/race catching it
- Restore
Paste the failing command output into GATE.md appendix (short).
Common gotchas
| Gotcha | Fix |
|---|---|
| Scope creep “full library” | One package, sharp edge |
| Fuzz only in comments | Real Fuzz function |
| Goldens non-deterministic | Stabilize or drop |
| No judgment section | Automatic gate fail |
Dirty go fix commit mix |
Separate chore commits |
| Generic + concurrent without docs | Mutex or “not safe” note |
Claiming 1.26 without go version |
Record actual version in GATE.md |
Checkpoint
- Rubric ≥ 80%
- Generic surface justified in writing
- Fuzz and/or golden present and useful
go test -race -count=10 ./...pass
- Go 1.26 quality notes recorded
- README enables a stranger to test
- Regression drill shows tests catching a deliberate break
- Honest list of what Stage V should improve
Commit
git add .
git commit -m "day38: stage IV gate — generics + deep tests + 1.26 pass"Write three personal gotchas before continuing.
Tomorrow
Stage V begins — Day 39 io interfaces: composition with Reader/Writer/Closer—stdlib that pays rent for every service you will build next.