Day 131 — Architecture Decision Records (ADRs)¶
Month 5 · Week 3 · ⬅ Day 130 · Day 132 ➡ · Journal index
🎯 Learning Objective¶
Record the why behind a significant design choice in a short, immutable Architecture Decision Record — capturing context, options, decision, and consequences.
📚 Topics¶
- What an ADR is and when one is warranted
- The Nygard template: Context · Decision · Status · Consequences
- Status lifecycle: Proposed → Accepted → Deprecated/Superseded (never edit, append)
- Storing ADRs in-repo (
docs/adr/NNNN-title.md); MADR;adr-tools
📖 Reading / Sources¶
- Michael Nygard — Documenting Architecture Decisions
- adr.github.io — ADR organization & tooling
- MADR — Markdown ADR template
- ThoughtWorks Tech Radar — "Lightweight ADRs" (Adopt)
📝 Notes¶
- An ADR captures one architecturally significant decision and, crucially, the context and consequences — the why, which code and diagrams never preserve → [[adr]].
- Write one when a choice is costly to reverse or non-obvious: a datastore, an API protocol (gRPC vs REST), a module boundary, an auth model. Skip it for routine, easily-reversed choices → [[significant-decision]].
- The Nygard template is four short sections: Context (forces at play), Decision (what we chose, active voice: "We will…"), Status, Consequences (trade-offs, both good and bad) → [[nygard-template]].
- ADRs are immutable: once Accepted, you don't rewrite history. To change course, write a new ADR that Supersedes the old one and flip the old one's status to Superseded by NNNN → [[immutable-log]].
- Status lifecycle: Proposed → Accepted → (later) Deprecated or Superseded. The set of ADRs is an append-only decision log a new teammate can read top-to-bottom → [[decision-log]].
- Keep them in the repo, version-controlled (
docs/adr/0001-record-architecture-decisions.md), numbered sequentially. They review through the same PR process as code → [[docs-as-code]]. - Keep each one short (one page). A wall of text doesn't get written or read; the value is the durable why, not exhaustive detail → [[lightweight]].
💻 Code Examples¶
ADRs are Markdown, not Go. The canonical Nygard skeleton:
# 7. Use gRPC for internal service-to-service APIs
Date: 2026-11-03
## Status
Accepted
## Context
Our services call each other over the network. We need a typed, versioned
contract, streaming support, and low latency. REST/JSON is untyped at the wire
and verbose; teams already share `.proto` schemas.
## Decision
We will use gRPC (protobuf over HTTP/2) for all internal service-to-service
APIs. External/public APIs stay REST via grpc-gateway transcoding.
## Consequences
+ Strongly-typed, versioned contracts; generated clients; streaming.
+ Built-in deadlines, metadata, and the status-code error model.
− Browsers can't call gRPC directly (need the gateway or gRPC-Web).
− Adds protoc/buf to the build toolchain and a learning curve for the team.
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
| (Writing, not code) Draft an ADR for this month's gRPC choice | ✅ | reasoning / Markdown only |
| (Writing) Draft an ADR for hexagonal layout (Day 127) | ✅ | reasoning / Markdown only |
🐛 Mistakes Made¶
- Edited an Accepted ADR in place when I changed my mind, erasing the original rationale. Reverted and wrote a superseding ADR instead — the history is the point.
- First ADR was three pages of design detail no one read. Trimmed to one page focused on context + consequences.
❓ Open Questions¶
- Granularity: one ADR per decision vs. one per epic? (Leaning per-decision so each is independently supersedable.)
🧠 Active Recall (answer without looking)¶
- Q: What are the four sections of a Nygard-style ADR, and which one is the most valuable later?
A
Context, Decision, Status, Consequences. The most valuable later is **Context** (plus Consequences) — it preserves the *why* and the trade-offs, which the code itself can never tell a future reader.- Q: You change a previously Accepted decision. What do you do to the old ADR?
A
Don't edit it. ADRs are immutable: write a NEW ADR that supersedes it, and mark the old one's status "Superseded by ADR-NNNN". The decision log stays an append-only history.🪶 Feynman Reflection¶
Code tells you what the system does; an ADR tells you why it does it that way — the alternatives you weighed and the prices you agreed to pay. Six months later when someone asks "why gRPC and not REST?", the answer isn't lost in a Slack thread; it's a numbered, dated note in the repo. And because you never erase an old decision — you only supersede it — the file list reads like a captain's log of how the architecture got here.
🕳️ Knowledge Gaps¶
- Tooling: whether
adr-tools/ MADR automation is worth it versus just a numbered Markdown convention.
✅ Summary¶
I can recognize when a decision deserves an ADR, write a tight Nygard-style record (Context/Decision/Status/Consequences), and maintain the log immutably by superseding rather than editing.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 132: a concrete architectural pattern in code — Redis cache-aside.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦⬜⬜⬜⬜ | 🟦🟦🟦🟦⬜ |
Suggested commit: docs(journal): architecture decision records (day 131)