Day 15 — Gate II: Concurrency & Generics Checkpoint

Updated

July 30, 2025

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.

Important

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:

  1. Generic helper or type that earns its type parameter (Day 33 judgment)
  2. Table-driven tests for core logic
  3. At least one of:
    • Fuzz target with seeds (+ any corpus crashers fixed)
    • Golden-file test with update workflow documented
  4. Race-clean tests: go test -race -count=10 ./...
  5. If the package is concurrent (optional but strong): cancel or limit test from Day 36
  6. README + short GATE.md (rubric self-score)
  7. 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 Map helpers
  • 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:

  1. What is parameterized, and what is intentionally concrete?
  2. What would have to change for a second type parameter to be justified?
  3. 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 present

All 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 int twice
  • 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 SyncSet with mutex and -race tests

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/day38

Write 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

  1. Complete generic API with constructors and zero-value story
  2. Table-driven tests for edge cases (empty, duplicate add, missing has)
  3. Fuzz or golden as required
  4. go test -race -count=10 ./...
  5. 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=20s

Lab 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):

  1. Why this is generic
  2. One invariant fuzz/golden enforces
  3. How -race is part of definition of done
  4. One Go 1.26 note you applied (new(expr), Green Tea default awareness, or go fix)

Then:

  1. Break one invariant on purpose
  2. Show test/fuzz/race catching it
  3. 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.