Skip to content

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

📝 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)

  1. Q: What are the four sections of a Nygard-style ADR, and which one is the most valuable later?
AContext, 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.
  1. Q: You change a previously Accepted decision. What do you do to the old ADR?
ADon'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)