Skip to content

Day 139 — Docs & ADRs

Month 5 · Week 4 · ⬅ Day 138 · Day 140 ➡ · Journal index

🎯 Learning Objective

Make the project legible: idiomatic godoc comments, a task-oriented README, and Architecture Decision Records (ADRs) that capture why the design is what it is.

📚 Topics

  • Go doc comments (doc.go, package/exported-symbol conventions)
  • README structure (quickstart, run, architecture diagram)
  • ADRs: context → decision → consequences; lightweight, append-only

📖 Reading / Sources

📝 Notes

  • Doc comments are godoc. A comment immediately above a package/func/type, starting with the symbol's name, becomes its documentation (go doc, pkg.go.dev). Write full sentences; the first sentence is the summary line → [[godoc]].
  • Put a package overview in a doc.go (or atop the primary file) for anything non-trivial — what the package is for and how to use it. Document the contract (preconditions, who closes what, goroutine-safety), not the obvious mechanics → [[package-docs]].
  • Document exported identifiers only as API; unexported helpers get short inline comments for the next reader. A // Deprecated: paragraph is a recognized convention tooling surfaces → [[exported-api]].
  • A README is task-oriented: one-line what-it-is, quickstart (go run ./cmd/server), how to send a request (grpcurl), config/env, an architecture sketch, and how to run tests. Optimize for someone who has 90 seconds → [[readme]].
  • ADRs record a single decision: Context (forces at play), Decision (what we chose), Status (proposed/accepted/superseded), Consequences (trade-offs accepted). They're short (~1 page), numbered, append-only — supersede rather than edit, so history is preserved → [[adr]] [[decision-record]].
  • ADRs answer the question code can't: why this and not the alternative? — e.g. "why Redis lists and not a real broker," "why hexagonal layering," "why at-least-once + idempotency." Future-you (and reviewers) stop re-litigating settled choices → [[design-rationale]].

💻 Code Examples

// Package queue provides an at-least-once background job queue backed by a Redis
// list. Producers enqueue jobs with Push; a worker pool drains them with retry,
// exponential backoff, and a dead-letter queue for jobs that exhaust their
// budget. All exported methods are safe for concurrent use and take a
// context.Context as their first argument for cancellation and deadlines.
//
// Delivery is at-least-once, so handlers MUST be idempotent (dedupe on Job.ID).
package queue
<!-- docs/adr/0003-redis-list-as-broker.md -->
# 3. Use a Redis list as the job broker

Status: Accepted — 2026-11-11

## Context
We need a simple, durable-enough background queue without operating Kafka/RabbitMQ.
Traffic is modest; at-least-once delivery is acceptable if handlers are idempotent.

## Decision
Use a Redis list with BRPOPLPUSH into a processing list for reliable pop, plus a
dead-letter list for exhausted jobs.

## Consequences
+ Tiny operational surface; one dependency we already run.
+ Reliable-enough delivery with a reaper for stuck processing entries.
− No native consumer groups/ordering guarantees; we own retry/DLQ logic.
− Revisit (supersede this ADR) if throughput outgrows a single Redis.

🏋️ Exercises / Practice

Exercise Status Link
Write a doc.go package overview for the queue package (project docs)
Author ADR-0001 (hexagonal) … ADR-0003 (Redis broker) (project docs/adr)

🐛 Mistakes Made

  • Wrote a doc comment that didn't start with the symbol name → it rendered oddly and go doc couldn't associate it. Started comments with Package queue … / Push ….
  • First ADR mixed three decisions into one file. Split them so each record is a single, supersedable choice.

❓ Open Questions

  • Do I keep ADRs in-repo (docs/adr/) only, or also publish a rendered index — and is an ADR linter worth it?

🧠 Active Recall (answer without looking)

  1. Q: What four sections make up an ADR, and why are ADRs append-only?
AContext (the forces/constraints), Decision (what was chosen), Status (proposed/accepted/superseded), and Consequences (trade-offs accepted). They're append-only so the historical reasoning is preserved; a changed decision is recorded by adding a new ADR that supersedes the old one, not by editing it.
  1. Q: What makes a Go comment show up as godoc, and what should the first sentence be?
AIt must directly precede the package/type/func/var declaration (no blank line) and begin with the identifier's name. The first sentence is the summary shown in listings, so it should be a concise, full-sentence description of what the symbol does.

🪶 Feynman Reflection

Docs are three layers for three readers. Godoc is for the developer calling your API right now — it states the contract. The README is for the newcomer with 90 seconds — run it, send a request, see the shape. ADRs are for the maintainer six months later asking "why on earth did we pick Redis?" — they freeze the reasoning so settled debates stay settled.

🕳️ Knowledge Gaps

  • Generating example tests (Example_*) that double as runnable, verified godoc examples.

✅ Summary

I can document a Go project at three levels: contract-stating godoc comments and a doc.go overview, a task-oriented README, and numbered, append-only ADRs that capture the design rationale future maintainers need.

⏭️ Next Steps / Prep for Tomorrow

  • Day 140: Month 5 review, spaced-repetition recall, and tag the release v0.5.0.

Time spent Difficulty Confidence
90 min 🟦⬜⬜⬜⬜ 🟦🟦🟦🟦⬜

Suggested commit: docs(project): godoc, README & architecture decision records (day 139)