Keeping docs alive: docs-as-code, owners and review checklists Intermediate
Estimated reading time: 5 min read
This page answers: why docs die, which mechanisms keep them alive, and what to check when reviewing a PR that touches an ADR or README.
Keeping docs alive
Section titled “Keeping docs alive”Nygard: “Large documents are never kept up to date.” Docs die because they live far from the code and nobody owns them. Fix it with process, not willpower.
Loading diagram…
| Mechanism | How | Why it works |
|---|---|---|
| Docs-as-code | Markdown in the repo, link lint in CI | Same version as the code; diffable and reviewable |
| Review in the same PR | PR template with a “Docs/ADR/runbook need updating?” item | The person who best understands the change writes the docs while it’s fresh |
| Owner | CODEOWNERS for docs/adr/ and docs/runbooks/ |
GitHub auto-requests the owner’s review when a PR touches their files (except draft PRs) |
| Expiry | Runbooks carry a “last updated”; review runbooks after every incident | A wrong runbook during an incident is worse than none |
| Delete aggressively | Delete docs describing things that no longer exist (git keeps the history) | Wrong docs erode trust in every other doc |
| Exception: ADRs | ADRs are never deleted, only superseded/deprecated | Their value is the history of decisions |
Example CODEOWNERS (GitHub: the last matching pattern takes precedence):
# .github/CODEOWNERS* @acme/checkout/docs/adr/ @acme/checkout-leads/docs/runbooks/ @acme/checkout @acme/srePros
- Docs in the repo: reviewable, versioned with code, available offline, greppable.
- Small, immutable ADRs: cheap to write, never “stale” in the sense of being untrue.
- Runbooks linked from alerts: less diagnosis time for people who didn’t write the code.
Cons
- People outside engineering (PMs, CS) find it hard to read/comment in the repo; design docs that need wide discussion may have to live in another tool.
- Requires review discipline: PR templates and CODEOWNERS only remind, they can’t enforce quality.
- A long decision log is hard to search: it needs an index and titles that are decision sentences.
When to adopt / exceptions
Section titled “When to adopt / exceptions”| Situation | What to do |
|---|---|
| Hard-to-reverse or convention-breaking decision, team of any size | Adopt ADRs. Start with ADR-0001 and the next decision; no need to backfill the whole history |
| An old decision keeps being questioned | Backfill selectively. State “written retroactively on date X, context based on Y’s memory” |
| Throwaway prototype/spike; a decision inside one module, reversible in one PR | Exception: a good commit message is enough |
| Company-level decisions (cloud choice, main language) | Exception: live in a shared repo/space; the service’s ADRs just link to them |
ADR checklist (when reviewing a PR that adds an ADR)
Section titled “ADR checklist (when reviewing a PR that adds an ADR)”- Is the title a decision sentence (“Publish events via an outbox instead of publishing directly”), not a noun (“Outbox”)?
- Does Context state a concrete problem and constraints, neutrally, without selling the solution?
- Are at least two options considered, each with a reason for rejection?
- Is the Decision in active voice, specific enough to check whether code follows it?
- Do the Consequences include at least one bad consequence and the work that comes with it (runbook, alert, idempotency requirement)?
- Is there a condition for revisiting (“when throughput exceeds…, when Kafka Connect is available”)?
- Are status, date, decision makers, supersede/related links complete? Is the number unique?
- Is it 1–2 pages long? If longer, the exploration belongs in a design doc.
README checklist for a backend service
Section titled “README checklist for a backend service”GitHub suggests a README answer: what the project does, why it’s useful, how to get started, where to get help, who maintains it. For an internal service, make that concrete:
- One paragraph: what the service does, what it does not do, which bounded context it belongs to.
- A quickstart that runs from a clean machine in a few commands (run it in CI or periodically so it doesn’t rot).
- Test, lint and migrate commands.
- A config/env var table, or a link to auto-generated reference.
- Links:
docs/architecture.md(C4),docs/adr/,docs/runbooks/, OpenAPI, dashboards. - Owner (team, chat channel), on-call rotation.
- Events published/consumed and APIs depended on, with schema links.
Self-check
Section titled “Self-check”- The team decides not to cache account balances. Should it write an ADR, and what should Context state?
- ADR-0012 chose outbox polling. A year later the team moves to CDC. Which files change, and how?
- Why must the outbox ADR’s Consequences mention idempotent consumers?
- The README explains why Kafka was chosen over RabbitMQ. Per Diátaxis, where should that part move?
- How do design docs and ADRs differ in when they are written and whether they are updated afterwards?