ADR - Architecture Decision Record (lightweight)
What this is: a short, durable note that captures why you chose what you chose - so the next person (and future you) isn’t reverse-engineering the reasoning from the code. This is Way #5: Keep it simple, write down the why, anchored in
ENG-PRIN-DOC-STMT(document decisions, not code - a decision must outlive the conversation it was made in).One decision per ADR. Half a page. AI may draft it; a human signs it (Way #2,
ENG-PRIN-REVIEW-STMT). Delete the quote-blocks before you commit it.
ADR-NNN: <short imperative title - the decision, not the topic>
Title the decision, e.g. “Compute team averages on the fly vs materialise them” - not “Team averages”. A good title reads like a verdict.
- Status: Proposed | Accepted | Superseded by ADR-NNN
- Date: YYYY-MM-DD
- Deciders: <who was in the room>
Context
What’s the situation and the forces at play? What makes this a decision rather than an obvious call? Keep it factual.
e.g. “The
/compareendpoint needs per-dimension team averages. The dataset is tiny (one team = 3 members, 6 dimensions). We could compute averages per request, or precompute and store (‘materialise’) them and refresh on write.”
Decision
State the choice in one sentence, active voice. Then the one or two reasons that actually drove it.
e.g. “We compute averages on the fly per request. The data is small enough that the query is trivially cheap, and on-the-fly removes a whole class of staleness bugs - there’s nothing to invalidate.” (
ENG-PRIN-SIMPLE-STMT- the simplest thing that works; don’t add a cache you don’t need yet.)
Alternatives considered
What else was on the table, and why it lost. One line each. This is the part future-you will thank you for.
| Option | Pro | Con | Verdict |
|---|---|---|---|
| Materialise + cache averages | Fast at scale | Cache invalidation on every score write; staleness risk | Rejected - premature for this dataset (YAGNI) |
| Compute on the fly | No staleness, no cache | Recomputes each request | Chosen - cheap at this size |
Consequences
What becomes easier, what becomes harder, and what you’re now on the hook to maintain. Be honest about the trade-off.
e.g. “Easier: no invalidation logic, simpler tests. Harder: if the org grows to thousands of members per scope, revisit and supersede this ADR. We accept that re-evaluation trigger explicitly.”
API & idempotency note (record this here, not in the code comments)
If the decision touches the API surface, note the contract implications - this is where REQ-API-6 reasoning lives.
e.g. “
/compareand/recommendare GET - safe and idempotent by definition (REQ-API-6), so noIdempotency-Keyheader is required; a client can retry freely. If either ever becomes a write (e.g. a futurePOST /compare/snapshot),REQ-API-6requires anIdempotency-Keyso retries after a dropped response don’t double-apply. We are recording that boundary now so the next person doesn’t have to rediscover it.” This mirrors the inline note already in../../../../stacks/nextjs/openapi.yaml- the ADR is where the reasoning lives; the OpenAPI file is where the contract lives. Keep them in sync (REQ-API-2).
One ADR earns you points in the Feature Sprint. The five ways: ways-of-working.md.
Downloads for this session
Grab the templates and sample files used here.