Weekly Review — Month 6 · Week 3 (Days 155–161)¶
📅 The Week in One Line¶
Turned a working service into a fast and hardened one: profile with pprof, eliminate allocations via escape analysis, recycle the rest with sync.Pool, prove it under load with k6/vegeta, scan it with govulncheck/gosec, and defend it with AuthN/Z and rate limiting — all measurement-first.
✅ What I Completed¶
- Day 155 — pprof: CPU (sampled) & heap (snapshot) profiling;
runtime/pprof+net/http/pprof;go tool pproftop/list - Day 156 — Escape analysis: stack vs heap,
-gcflags=-m,-benchmem/AllocsPerRun, append-into-buffer, dead-code-elimination trap - Day 157 —
sync.Pool: reuse hot-path objects; Reset-after-Get; pointers only; GC may drain; drop outliers - Day 158 — Load testing: vegeta (open model) vs k6 (closed/arrival-rate); p50/p95/p99; coordinated omission; the knee
- Day 159 —
govulncheck(reachable CVEs, source/binary modes) +gosec(insecure patterns); honest triage; CI gating - Day 160 — AuthN vs AuthZ; constant-time compare; identity in
contextvia private key type;401/403/429; token-bucket rate limiting - Day 161 — Week review + recall
- Stdlib examples:
pprof,escape,syncpool,ratelimit - Exercises solved: 3 (
tokenbucket,allocfree,bufpool) — allgo testgreen
💡 Lessons Learned¶
- Measure before optimizing. Profile → cut allocations → pool, in that order. A pool added before a profile is a guess.
- CPU profiles are statistical samples; heap profiles are live snapshots — different questions (where's time vs what's retained), and
runtime.GC()before a heap dump matters. - "Pointer" doesn't mean "heap." Escape analysis keeps a local
*Ton the stack as long as it doesn't outlive the frame;-gcflags=-mis the source of truth. sync.Poolobjects are not zeroed and the GC may drain the pool — Reset after Get, keepNewcheap, store pointers, never use after Put.- Report percentiles, not means; avoid coordinated omission with an open/arrival-rate load model.
govulncheckis reachability-based (low noise) and complementsgosec(insecure code patterns) — different layers.- Constant-time compares (
crypto/subtle) and unexported context-key types are small habits that close real holes (timing leak, key collision).
💪 Strengths (what clicked)¶
- The six-step pipeline (profile → escape → pool → load → scan → harden) reads as one coherent method.
testing.AllocsPerRunas a quick alloc oracle — and remembering the sink to defeat DCE.- The token-bucket algorithm and its
401/403/429middleware ordering are now writable from memory.
🧩 Weaknesses (what's still fuzzy)¶
- Reading flame graphs /
go tool pprof -httpandgo tool tracefluently on real profiles. benchstatworkflow for statistically comparing before/after.- Distributed rate limiting (shared bucket) and JWT/PASETO validation pitfalls.
🔁 Spaced-Repetition Re-quiz (topics from earlier weeks)¶
- Q: (Slices, M1) Why can
appendmutate a slice the caller still holds, and when does it stop?A
If the backing array has spare capacity, append writes in place, so an aliasing slice sees the change. Once it must grow, it allocates a new array and the two slices decouple. Pass a re-sliced copy or slices.Clone when you must avoid aliasing.
2. Q: (Channels, M2) Send/recv/close behavior on nil vs closed channels? A
nil: send and recv block forever; closing panics. Closed: send panics, recv returns the zero value immediately with ok==false, closing again panics.
3. Q: (Errors, M3) errors.Is vs errors.As? A
errors.Is walks the chain testing for a target sentinel value. errors.As walks the chain for the first error assignable to a target type and assigns it so you can read its fields.
4. Q: (Context, M5) Where does ctx go and what must you never do with it? A
First parameter, named ctx. Never store it in a struct for later, never pass nil (use context.TODO()), and only use context.Value for request-scoped data with an unexported key type.
5. Q: (Production, M6 W2) Why does CGO_ENABLED=0 unlock FROM scratch/distroless? A
It forces a pure-Go static binary with no dynamic libc dependency, so a minimal image with no shared libraries can still run it. A CGO-linked binary needs glibc and crashes on scratch.
🎯 Action Items¶
- Add
go test -bench=. -benchmem+benchstatto compare a before/after optimization on a real handler. - Wire
govulncheck ./...andgosec ./...as blocking CI steps (reachable HIGH/CRITICAL fails the build). - Put the rate-limit + auth middleware (constant-time compare,
429+Retry-After) in the capstone server. - Practice
go tool pprof -httpandgo tool traceon a load-test capture.
🚀 Next Week Goals¶
- Capstone (Week 4): assemble architecture (ports & adapters), REST/gRPC API, Postgres + Redis, and the Week-1 observability stack into one deployable service.
- Apply this week's discipline: profile the capstone, gate it with the scanners, and defend it with the limiter/auth from Day 160.
📊 Metrics¶
| Hours | Days hit | Exercises | Commits | Avg confidence |
|---|---|---|---|---|
| 9.5 | 7/7 | 3 | 7 | 3.⅗ |
Suggested commit: docs(journal): month 6 week 3 review