Decision Documentation
Write the ADR so a future engineer (or you in 6 months) can understand not just what was decided, but why.
Why
An ADR that says 'we chose Postgres' is useless — in 6 months someone will ask 'why not DynamoDB?' and the reasoning is gone. An ADR that says 'we chose Postgres because we need joins + transactions, DynamoDB was rejected because our access patterns are relational, and we considered MySQL but the team knows Postgres' is useful forever. Decision documentation is about capturing the context, alternatives, and reasoning at decision time, so the decision can be audited, understood, and superseded (with a new ADR) when the context changes. Without it, every decision is re-litigated from scratch because no one knows why it was made.
When
For every moderate+ decision, at decision time. Takes 30-60 min to write. Never retroactively — memory rots.
How
1. Use the ADR template (Nygard format): # ADR-NNN: Title ## Status: Proposed | Accepted | Superseded by ADR-MMM ## Context: the problem, the forces, the constraints ## Decision: what we decided ## Consequences: positive + negative + neutral ## Alternatives considered: what else + why rejected 2. Write the Context first — it's the part that rots fastest; capture the problem + constraints 3. Be specific about alternatives: "DynamoDB: rejected because access patterns need joins" not "DynamoDB: not a fit" 4. Note the consequences you're accepting (trade-offs made), not just the benefits 5. Link to the criteria + scores if you used decision-criteria 6. Number sequentially; supersede, don't edit — if the decision changes, write ADR-MMM that supersedes NNN 7. Store ADRs in /docs/adr/ in the repo, not a wiki — they version with the code
Example
# ADR-015: Use Postgres for the orders service ## Status: Accepted (2024-03-12) ## Context The orders service needs to store orders + line items with ACID transactions (oversell is a legal issue). Access patterns: fetch by id, list by user+date, aggregate by month. Team knows Postgres, not DynamoDB. ## Decision Use Postgres (RDS managed, db.r6g.large). ## Consequences + Joins + transactions for free + Team can operate it (existing expertise) − Vertical scaling limit (mitigated: read replicas for list queries) − Single-region (accepted: orders are regional anyway) ## Alternatives - DynamoDB: rejected — access patterns are relational (joins), not KV - MySQL: rejected — team expertise is Postgres, no MySQL advantage here - MongoDB: rejected — transactions are weaker, access patterns are relational
Common Problems
- ⚠Editing an accepted ADR — the history is lost; supersede with a new ADR that references the old, so the evolution is traceable.
Best Practices
- ✓Write the Context section as if the reader wasn't there — in 6 months no one will remember the forces that drove the decision.
- ✓Keep ADRs short (1-2 pages) — if it's longer, the decision is too big; decompose into multiple ADRs.