Skip to content

Weekly Review — Month 2 · Week 4 (Days 050–056)

Journal index · Roadmap › Month 2 Week 4

📅 The Week in One Line

The engineering around the code: catch bugs with go vet/golangci-lint, automate the workflow with a Makefile, ship variants via build tags & cross-compilation, profile with pprof, enforce it all in GitHub Actions CI, and land a stdlib-only URL-shortener capstone.

✅ What I Completed

  • Day 050 — go vet & golangci-lint / staticcheck (analyzers, .golangci.yml, //nolint)
  • Day 051 — Makefile & task automation (.PHONY, tab rule, -ldflags -X version stamp)
  • Day 052 — Build tags & cross-compilation (//go:build, filename suffixes, GOOS/GOARCH) — build-tags example
  • Day 053 — pprof quickstart (runtime/pprof files + net/http/pprof live, flat vs cum) — pprof example
  • Day 054 — GitHub Actions CI for Go (setup-go caching, matrix, gofmt -l gate)
  • Day 055 — Project: URL shortener (base62, RWMutex store, 302 redirects) — urlshortener example
  • Day 056 — Month 2 review + recall, tag v0.2.0
  • Runnable examples: 3 (build-tags, pprof, urlshortener)
  • Exercises solved: 3 (base62, normalizeurl, urlstore) — round-trip property test + -race

💡 Lessons Learned

  • go vet runs automatically inside go test; a vet failure fails the test run — clean vet is table stakes, and golangci-lint/staticcheck are external binaries you run as tools, never imports.
  • A Makefile recipe must start with a literal tab, each line runs in its own shell, and task names belong under .PHONY so a same-named file can't make make skip them.
  • Build constraints resolve at compile time: //go:build needs a blank line before package, per-OS files must be mutually exclusive + exhaustive, and runtime.GOOS reports the build target, not the host.
  • pprof CPU profiles are only valid after StopCPUProfile; net/http/pprof is a blank import for its init; read flat (own time) vs cum (incl. callees) to find the real hot spot.
  • gofmt -l . alone exits 0 even when it lists files — the CI gate must be test -z "$(gofmt -l .)".
  • Maps aren't safe for concurrent writes; the shortener's store needs an RWMutex (readers RLock, writers Lock), proven with go test -race.

💪 Strengths (what clicked)

  • Wiring the local gate (fmtvettest -racebuild) into both a Makefile and CI — the same checklist, enforced two ways.
  • Build tags + cross-compilation: the "one of N files is linked" mental model finally feels obvious.
  • Designing the shortener around an interface-friendly, lock-guarded store.

🧩 Weaknesses (what's still fuzzy)

  • Deeper pprof views (alloc_space vs inuse_space, block/mutex profiles) and when to switch to runtime/trace.
  • Exhaustively reasoning about build-constraint combinations (OS × arch × custom tags) without trial and error.
  • CI niceties: artifact upload, required checks, reusable workflows.

🔁 Spaced-Repetition Re-quiz (topics from earlier weeks)

  1. Q: (Wk1) What does io.Copy return, and how do you cap how much it reads?
    A

It returns (written int64, err error) and copies until EOF (EOF itself isn't reported as an error). Cap it by wrapping the source in io.LimitReader(r, n). 2. Q: (Wk2) What's the reference time layout string in time.Format?

A

Mon Jan 2 15:04:05 MST 2006 — i.e. 2006-01-02 15:04:05. The numbers 1,2,3,4,5,6,7 encode month/day/hour/min/sec/year/zone.

3. Q: (Wk2) Where does context.Context go in a function signature, and how do you propagate cancellation to an HTTP request?
A

It's the first parameter, conventionally named ctx. Use http.NewRequestWithContext(ctx, ...) so the request is cancelled when the context is.

4. Q: (Wk3) In a table-driven test with subtests, why capture the loop variable before t.Parallel() (pre-Go 1.22)?
A

Parallel subtests run after the loop advances; without tt := tt they'd all see the final iteration's value. Go 1.22 made the loop var per-iteration, but capturing is still the safe, explicit habit.

5. Q: (Wk4) Why is fmt.Printf("%d", "x") a compile-success but a bug?
A

Printf takes ...any, so any type compiles; the verb/arg contract is unchecked by the compiler. go vet's printf analyzer flags the mismatch statically.

🎯 Action Items

  • Add a real Makefile + .github/workflows/ci.yml at the repo root (currently snippets in the notes).
  • Profile the shortener under load and record a flat/cum read-through.
  • Practice GOOS/GOARCH cross-builds for windows/amd64 and darwin/arm64 and check the binaries run.

🚀 Next Week Goals

  • Begin Month 3: deeper concurrency patterns (worker pools, errgroup-style fan-out, pipelines, graceful shutdown).
  • Evolve the shortener toward a real service (persistence behind an interface, middleware, http.Server.Shutdown).

📊 Metrics

Hours Days hit Exercises Commits Avg confidence
10.5 7/7 3 7 3.⅗

Suggested commit: docs(journal): week 4 review