Skip to content

Weekly Review — Month 4 · Week 4 (Days 106–112)

Journal index · Roadmap › this week

📅 The Week in One Line

Built the Month 4 capstone — a layered, tested, documented, containerised REST service — and shipped v0.4.0.

✅ What I Completed

  • Day 106 — Layered architecture: inward-pointing layers, consumer-owned interfaces
  • Day 107 — Handlers & services: thin transport, rule-owning service, JSON helpers
  • Day 108 — Repository & migrations: SQL adapter hides the driver behind domain errors
  • Day 109 — Integration tests: httptest + testcontainers, build-tag gated
  • Day 110 — OpenAPI docs: 3.0 document, $ref schemas, served /openapi.json
  • Day 111 — Dockerfile & CI: multi-stage static image, go test -race pipeline
  • Day 112 — Month 4 review + v0.4.0 tag
  • Mini-project: layered Users/Notes REST API (capstone)
  • Exercises solved: 3 (service, httpapi, openapi)

💡 Lessons Learned

  • Interfaces belong with their consumer; that one rule makes the whole stack swappable and testable.
  • The repository is the single place driver errors (sql.ErrNoRows) become domain errors (ErrNotFound); the handler is the single place domain errors become HTTP status.
  • httptest.NewServer lets you integration-test the real stack with no extra dependency; testcontainers only swaps in a real database — the driving loop is identical.
  • A multi-stage build + CGO_ENABLED=0 + distroless turns a 900 MB image into ~15 MB and removes the shell from prod.
  • OpenAPI is just JSON; modelling it as structs gives deterministic, diffable output you can test.

💪 Strengths (what clicked)

  • Wiring layers bottom-up with constructor injection felt natural after the repository/txn examples from Weeks 2–3.
  • Table-driven handler tests with httptest.NewRecorder are fast and read well.

🧩 Weaknesses (what's still fuzzy)

  • pgx-native API vs database/sql compatibility layer — when each is worth it.
  • Parallelising integration tests safely (schema-per-test vs transaction-per-test).

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

  1. Q: (Wk1) What does Go 1.22's ServeMux do automatically when a path matches but the method doesn't?
    AReturns 405 Method Not Allowed (with an Allow header) instead of falling through to 404.
  2. Q: (Wk2) Why is *sql.DB long-lived and shared rather than opened per request?
    AIt's a connection pool, safe for concurrent use; opening one per request defeats pooling and exhausts connections.
  3. Q: (Wk3) When do you return 400 vs 422 for a bad request body?
    A400 for malformed/unparseable JSON; 422 for well-formed JSON that fails validation rules.
  4. Q: (Wk3) How do you compare an HMAC/JWT signature without leaking timing?
    Ahmac.Equal, a constant-time comparison — never == on the bytes.
  5. Q: (this week) Where should sql.ErrNoRows be translated and to what?
    AIn the repository, wrapped to a domain ErrNotFound via %w, so callers stay driver-agnostic and match with errors.Is.

🎯 Action Items

  • Add govulncheck to the CI workflow.
  • Document the JWT securityScheme in the OpenAPI spec.
  • Benchmark pgx-native vs database/sql for the hot read path.

🚀 Next Week Goals

  • Start Month 5 (theme TBD in the month review): pick the project, read the roadmap, scaffold day 113.
  • Carry the layered-architecture habits forward into the new project.

📊 Metrics

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

Suggested commit: docs(journal): week 4 review