How to Write Architecture Decision Records (ADRs) That Actually Get Read
How to Write Architecture Decision Records (ADRs) That Actually Get Read
Introduction
Six months from now, someone will ask: "Why did we build it this way?"
If you're lucky, someone who was there will remember. If you're not, you'll spend hours in Slack archaeology, git blame expeditions, and increasingly desperate searches through Google Docs named "Architecture Thoughts v3 FINAL (2)".
Architecture Decision Records (ADRs) solve this problem. They're short documents that capture why you made a decision, not just what you decided. Done well, they become an invaluable institutional memory. Done poorly, they become another pile of unread documents that no one trusts.
This guide is about writing ADRs that people actually read, reference, and value—not ADRs that satisfy a process checkbox.
What Is an ADR?
ARCHITECTURE DECISION RECORD (ADR):
════════════════════════════════════════════════════════════════════
A short document that captures a single architectural decision,
including the context that led to it and the consequences of making it.
KEY PROPERTIES:
───────────────
• SHORT: 1-2 pages, not 20
• SINGLE: One decision per document
• IMMUTABLE: Captures point-in-time thinking (supersede, don't edit)
• CONTEXTUAL: Explains WHY, not just WHAT
WHAT AN ADR IS NOT:
───────────────────
✗ A complete design document
✗ A requirements spec
✗ A project proposal
✗ A wiki page that gets updated forever
✗ A rubber stamp for decisions already made
THE ANATOMY OF AN ADR:
════════════════════════════════════════════════════════════════════
┌─────────────────────────────────────────────────────────────────┐
│ │
│ TITLE │
│ ADR-001: Use PostgreSQL as primary database │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ STATUS │
│ Accepted | Superseded | Deprecated | Rejected │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ CONTEXT │
│ What's the situation? What forces are at play? │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ DECISION │
│ What did we decide? │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ CONSEQUENCES │
│ What are the results? Good and bad. │
│ │
└─────────────────────────────────────────────────────────────────┘
Why ADRs Matter
┌─────────────────────────────────────────────────────────────────────┐
│ THE VALUE OF ADRS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ FOR NEW TEAM MEMBERS │
│ ───────────────────────────────────────────────────────────────── │
│ "Why is the system built this way?" │
│ → Read the ADRs. Understand the history in hours, not months. │
│ │
│ FOR FUTURE YOU │
│ ───────────────────────────────────────────────────────────────── │
│ "Why did we decide this 18 months ago?" │
│ → Read the ADR. Remember the context you've forgotten. │
│ │
│ FOR AVOIDING REPEATED DEBATES │
│ ───────────────────────────────────────────────────────────────── │
│ "Should we reconsider using X instead of Y?" │
│ → Check ADR-023. We evaluated X. Here's why we chose Y. │
│ → If context has changed, write a new ADR superseding it. │
│ │
│ FOR ACCOUNTABILITY │
│ ───────────────────────────────────────────────────────────────── │
│ "Who made this decision and why?" │
│ → It's documented. Named. With reasoning. │
│ │
│ FOR LEARNING │
│ ───────────────────────────────────────────────────────────────── │
│ "Did our past decisions work out?" │
│ → Compare predictions in Consequences to reality. │
│ → Learn from what we got right and wrong. │
│ │
└─────────────────────────────────────────────────────────────────────┘
Why Most ADRs Don't Get Read
Before we fix the problem, let's understand it.
REASONS ADRS GO UNREAD:
════════════════════════════════════════════════════════════════════
1. TOO LONG
───────────────────────────────────────────────────────────────────
"I'll read it later" → Never reads it.
The problem: 10-page ADRs with exhaustive detail.
The reality: People have 5 minutes, not 50.
2. TOO VAGUE
───────────────────────────────────────────────────────────────────
"We decided to use microservices for scalability."
The problem: No specifics. Reads like a buzzword bingo card.
The reality: Vague ADRs don't help anyone make decisions.
3. WRITTEN AFTER THE FACT
───────────────────────────────────────────────────────────────────
"We already built it, now document why."
The problem: Rationalization, not reasoning.
The reality: People can tell. Trust erodes.
4. UNFINDABLE
───────────────────────────────────────────────────────────────────
"We have ADRs... somewhere."
The problem: Scattered across wikis, repos, drives.
The reality: If it takes 10 minutes to find, no one will look.
5. NEVER UPDATED
───────────────────────────────────────────────────────────────────
"This ADR is from 2019 and we changed it all in 2021."
The problem: Outdated ADRs are worse than no ADRs.
The reality: Wrong information destroys trust in documentation.
6. NO ONE ASKED FOR THEM
───────────────────────────────────────────────────────────────────
"We should write ADRs" → Writes 50 → No one reads them.
The problem: Process for process's sake.
The reality: ADRs only work if there's a culture of using them.
7. BORING
───────────────────────────────────────────────────────────────────
"The committee evaluated options based on criteria..."
The problem: Corporate-speak kills engagement.
The reality: Humans respond to stories, not reports.
The ADR Template That Works
The Essential Structure
The best ADR format is Michael Nygard's original, with some enhancements:
# ADR-NNN: [Short Title of Decision]
**Status:** [Proposed | Accepted | Deprecated | Superseded by ADR-XXX]
**Date:** YYYY-MM-DD
**Deciders:** [Names of people involved]
**Technical Story:** [Optional: Link to ticket/RFC]
## Context
[What is the situation? What forces are at play? What is the problem
or opportunity? Be specific. Include constraints, requirements, and
relevant facts. A reader should understand the situation without
any prior context.]
## Decision
[What are we going to do? State it clearly and directly. Start with
"We will..." to make it unambiguous. Include enough detail that
someone could verify the decision was followed.]
## Consequences
[What are the results of this decision? Include both positive and
negative consequences. Be honest about tradeoffs. What becomes
easier? What becomes harder? What risks are we accepting?]
## Alternatives Considered
[Optional but valuable: What other options did we evaluate? Why
didn't we choose them? This prevents relitigating the decision.]
Template in Practice
# ADR-042: Use Redis for Session Storage
**Status:** Accepted
**Date:** 2024-01-15
**Deciders:** Alice Chen, Bob Smith, Carol Davis
**Technical Story:** PROJ-1234
## Context
Our application currently stores sessions in-memory on each web server.
This causes problems:
1. **Lost sessions on deploy:** Every deployment logs out all users (~50K)
2. **No horizontal scaling:** Users must stick to one server (sticky sessions)
3. **Memory pressure:** Sessions consume 2GB RAM per server, limiting capacity
We need a shared session store that:
- Supports our current session volume (~500K active sessions)
- Has sub-10ms read latency (sessions read on every request)
- Handles 10K reads/second and 1K writes/second
- Can be deployed in our AWS environment
## Decision
We will use **Redis** (AWS ElastiCache) as our session storage backend.
Specifically:
- Deploy a 2-node Redis cluster (primary + replica) in ElastiCache
- Use `r6g.large` instances (13GB memory, sufficient for 500K sessions)
- Set session TTL to 24 hours to match current behavior
- Implement automatic failover via ElastiCache Multi-AZ
Session data will be serialized as JSON with the key format:
`session:{session_id}`.
## Consequences
**Positive:**
- Deployments no longer log out users
- Can horizontally scale web servers without sticky sessions
- Reduced memory pressure on web servers (~1.5GB freed per server)
- Sub-ms session reads based on our testing
**Negative:**
- New infrastructure dependency (Redis must be highly available)
- Additional cost: ~$300/month for ElastiCache cluster
- Team needs to learn Redis operations
- Network latency added to every request (~1ms, acceptable)
**Risks we're accepting:**
- Redis failure = all users logged out (mitigated by Multi-AZ)
- Session data now stored outside application (security consideration)
## Alternatives Considered
**1. Database-backed sessions (PostgreSQL)**
Rejected: Adds ~5-10ms per request. Database is already at capacity.
**2. JWT tokens (stateless sessions)**
Rejected: Can't revoke sessions server-side. Token size concerns.
Would require significant auth refactoring.
**3. Memcached**
Rejected: No persistence, no replication in our setup. Redis has
better tooling and team familiarity.
**4. Do nothing**
Rejected: Problems are getting worse as we grow. Deploy disruption
is already causing customer complaints.
Writing Each Section Well
The Title
THE TITLE:
════════════════════════════════════════════════════════════════════
The title should be a complete thought that tells you the decision.
BAD TITLES:
───────────────
• "Database" (What about it?)
• "Session Storage Decision" (What did we decide?)
• "ADR for Caching Implementation Approach Selection" (Verbose noise)
• "RFC-042" (Not a title, just a number)
GOOD TITLES:
────────────
• "Use PostgreSQL as primary database"
• "Store sessions in Redis instead of in-memory"
• "Adopt TypeScript for all new frontend code"
• "Keep monolith, delay microservices migration"
FORMAT:
───────
ADR-NNN: [Verb] [Object] [Optional: Instead of Alternative]
Examples:
• ADR-001: Use PostgreSQL for primary database
• ADR-012: Adopt React instead of Vue for web UI
• ADR-023: Reject GraphQL in favor of REST
• ADR-034: Defer event sourcing to 2025
The Status
ADR STATUS OPTIONS:
════════════════════════════════════════════════════════════════════
PROPOSED
────────
Decision is being discussed. Not yet accepted.
Use: Active debate, seeking input.
ACCEPTED
────────
Decision is made and in effect.
Use: Most ADRs end up here.
DEPRECATED
──────────
Decision is no longer recommended but may still exist in codebase.
Use: Phasing out, but not actively replaced.
SUPERSEDED BY ADR-XXX
─────────────────────
A newer decision replaces this one.
Use: When context changed and we made a new decision.
Always link to the new ADR!
REJECTED
────────
We considered this but decided not to do it.
Use: Document why we chose NOT to do something.
Prevents relitigating the same debate.
LIFECYCLE:
────────────────────────────────────────────────────────────────────
Proposed ──────► Accepted ──────► Superseded by ADR-XXX
│ │
│ └──────────► Deprecated
│
└──────────────► Rejected
NOTE: Don't delete or edit old ADRs. They're historical records.
If context changes, write a new ADR that supersedes the old one.
The Context Section
The Context is where most ADRs fail. This is the most important section.
CONTEXT: THE MOST IMPORTANT SECTION
════════════════════════════════════════════════════════════════════
Purpose: Help the reader understand WHY this decision was needed.
A good Context section answers:
• What problem are we solving?
• What constraints exist?
• What forces are pushing us toward a decision?
• What's the current situation?
• Why now?
BAD CONTEXT:
────────────────────────────────────────────────────────────────────
"We need to choose a database for our application. There are
several options available in the market. We need to make a decision."
Problems:
• Tells us nothing about the actual situation
• No constraints or requirements
• No forces at play
• Could apply to any project ever
GOOD CONTEXT:
────────────────────────────────────────────────────────────────────
"Our B2B SaaS application stores customer data with these
characteristics:
• ~100 tables with complex relationships (order → line items → products)
• Heavy read workload (95% reads, 5% writes)
• Strict data consistency requirements (financial data)
• Multi-tenant with ~500 customers, ranging from 1K to 5M rows each
• Team has deep PostgreSQL experience; no MongoDB expertise
Our current SQLite setup works for development but doesn't support
concurrent writes needed for production (we're seeing write lock
timeouts during load testing).
We need a production database that:
1. Handles concurrent writes from multiple servers
2. Supports ACID transactions (we have cross-table consistency needs)
3. Can be operated by our small team (no dedicated DBA)
4. Works in AWS (our deployment environment)
5. Fits within our infrastructure budget (~$500/month initially)"
Why this works:
• Specific to THIS project
• Clear constraints
• Quantified where possible
• Explains the actual forcing function (SQLite limitations)
• Tells you what matters (consistency, team skills, budget)
CONTEXT CHECKLIST:
────────────────────────────────────────────────────────────────────
□ Would someone outside the team understand the situation?
□ Are the constraints and requirements clear?
□ Is it obvious why a decision is needed?
□ Are relevant numbers included (scale, timeline, budget)?
□ Is the current state described?
The Decision Section
DECISION: CLARITY ABOVE ALL
════════════════════════════════════════════════════════════════════
Purpose: State what we're doing. Unambiguously.
Rules:
• Start with "We will..."
• Be specific enough to verify
• Include implementation details that matter
• Don't hedge ("We should probably...")
BAD DECISION:
────────────────────────────────────────────────────────────────────
"After careful consideration of the various options, we have
determined that a NoSQL solution would best meet our needs,
and we are leaning toward MongoDB as a potential candidate."
Problems:
• Hedging ("leaning toward," "potential")
• Vague ("NoSQL solution")
• Corporate-speak obscures the actual decision
GOOD DECISION:
────────────────────────────────────────────────────────────────────
"We will use PostgreSQL 15 as our primary production database.
Specifically:
• Deploy on AWS RDS (db.r6g.large initially, ~$200/month)
• Use connection pooling via PgBouncer (already in our stack)
• Implement row-level security for multi-tenant isolation
• Use our existing migration framework (Flyway)
We will NOT use:
• MongoDB or other document stores
• Aurora (cost/complexity tradeoff not justified at our scale)"
Why this works:
• Unambiguous ("We will use PostgreSQL 15")
• Specific version
• Implementation details included
• Explicitly states what we're NOT doing
• Verifiable (you can check if this was followed)
DECISION CHECKLIST:
────────────────────────────────────────────────────────────────────
□ Does it start with "We will..."?
□ Could someone verify this decision was followed?
□ Are key implementation details included?
□ Is it clear what we are NOT doing?
□ Would two people reading this understand the same thing?
The Consequences Section
CONSEQUENCES: HONESTY REQUIRED
════════════════════════════════════════════════════════════════════
Purpose: Document what happens as a result of this decision.
Good AND bad. Honest about tradeoffs.
Every decision has tradeoffs. If your Consequences section is
all positive, you're not being honest.
BAD CONSEQUENCES:
────────────────────────────────────────────────────────────────────
"This decision will improve our system's performance, reliability,
and maintainability while reducing costs."
Problems:
• All positive (suspicious)
• Vague (how much improvement?)
• No tradeoffs acknowledged
GOOD CONSEQUENCES:
────────────────────────────────────────────────────────────────────
"**Positive:**
• ACID transactions ensure data consistency across multi-table writes
• Team's PostgreSQL experience means faster development and debugging
• RDS handles backups, patches, and failover automatically
• Cost predictable at ~$200/month initially, scaling with instance size
• Strong ecosystem: tooling, libraries, documentation
**Negative:**
• Schema migrations require careful planning (harder than schemaless)
• Vertical scaling has limits (may need read replicas at ~10K QPS)
• Team must learn row-level security patterns (new to us)
• Vendor lock-in to AWS RDS (mitigated: PostgreSQL itself is portable)
**Risks we're accepting:**
• RDS single-AZ initially (cost tradeoff; manual failover if region dies)
• Row-level security adds complexity; bugs here could leak tenant data
**What becomes easier:**
• Complex queries and joins
• Enforcing referential integrity
• Using standard ORM patterns
**What becomes harder:**
• Schema changes require migrations
• Scaling beyond single-node capacity"
Why this works:
• Honest about both sides
• Specific about tradeoffs
• Acknowledges risks explicitly
• Helps future readers understand what to watch for
CONSEQUENCES CHECKLIST:
────────────────────────────────────────────────────────────────────
□ Are there negative consequences? (If not, think harder)
□ Are tradeoffs explicit?
□ What risks are we accepting?
□ What becomes easier? What becomes harder?
□ Are consequences specific, not generic?
The Alternatives Section
ALTERNATIVES: PREVENT RELITIGATING
════════════════════════════════════════════════════════════════════
Purpose: Document what else we considered and why we rejected it.
Prevents the same debate happening again.
Optional but valuable. Include when:
• There were serious alternatives
• Someone will ask "why not X?"
• The rejection reason isn't obvious
BAD ALTERNATIVES:
────────────────────────────────────────────────────────────────────
"We also considered MongoDB and DynamoDB."
Problems:
• No reasoning
• Doesn't prevent the debate from recurring
GOOD ALTERNATIVES:
────────────────────────────────────────────────────────────────────
"**MongoDB**
Rejected: Our data is highly relational (orders → line items →
products). MongoDB would require denormalization that increases
storage costs and complicates updates. Team has no MongoDB
experience.
**DynamoDB**
Rejected: Access patterns aren't predictable enough. We'd need
to design partition keys carefully for queries we haven't written
yet. Vendor lock-in is stronger than RDS.
**CockroachDB**
Rejected: Interesting for distributed SQL, but we don't need
global distribution. Adds operational complexity we can't justify
at our scale. Higher cost.
**SQLite (status quo)**
Rejected: Write lock timeouts at 50 concurrent users. Can't scale
to production requirements. Must change."
INCLUDE:
────────
• The alternative considered
• Why it was rejected (specific to our context)
• When it might be reconsidered (if relevant)
When to Write an ADR
ADR Triggers
WHEN TO WRITE AN ADR:
════════════════════════════════════════════════════════════════════
ALWAYS WRITE AN ADR WHEN:
─────────────────────────
□ Choosing technology (database, framework, language, tool)
□ Deciding on architecture pattern (monolith vs microservices)
□ Making cross-cutting changes (auth approach, API design)
□ Setting standards that future code must follow
□ Making expensive-to-reverse decisions
□ Explicitly deciding NOT to do something
PROBABLY WRITE AN ADR WHEN:
───────────────────────────
□ Team debated significantly before deciding
□ Decision affects multiple components or teams
□ Someone will ask "why did we do this?"
□ Onboarding would be faster with documentation
DON'T WRITE AN ADR FOR:
───────────────────────
□ Implementation details within a component
□ Decisions that are easily reversible
□ Standard practices that don't need justification
□ Decisions already documented elsewhere (use links)
THE DECISION SIZE TEST:
════════════════════════════════════════════════════════════════════
Would you need to justify this decision to:
• A new team member? → Probably ADR
• Your future self in 6 months? → Probably ADR
• Someone in a code review? → Maybe not
Is this decision:
• Expensive to change? → ADR
• Affecting architecture? → ADR
• A one-time implementation detail? → Not ADR
ADR vs Other Documents
ADR vs RFC vs DESIGN DOC:
════════════════════════════════════════════════════════════════════
RFC (Request for Comments)
──────────────────────────
Purpose: Propose and discuss a change before deciding
When: Before decision is made
Length: Medium to long (full proposal, detailed design)
Outcome: Decision (captured in ADR)
ADR (Architecture Decision Record)
──────────────────────────────────
Purpose: Record that a decision was made, and why
When: After decision is made (or to capture retrospectively)
Length: Short (1-2 pages)
Outcome: Reference document for future
DESIGN DOC
──────────
Purpose: Detailed technical design for implementation
When: After decision, before/during implementation
Length: Long (full technical specification)
Outcome: Implementation guidance
HOW THEY FIT TOGETHER:
────────────────────────────────────────────────────────────────────
┌──────────────────────────────────────────────────────────────────┐
│ │
│ RFC ADR Design Doc │
│ "Should we use "We decided to "Here's how │
│ Redis?" use Redis" we'll implement│
│ Redis" │
│ │ │ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Discussion Decision Record Implementation │
│ & Debate & Context Reference │
│ │
└──────────────────────────────────────────────────────────────────┘
For smaller decisions: Just an ADR (skip RFC and design doc)
For larger decisions: RFC → ADR → Design Doc (if complex)
Making ADRs Discoverable
The best ADR, unread, is worthless.
Where to Store ADRs
OPTION 1: IN THE REPOSITORY (Recommended)
════════════════════════════════════════════════════════════════════
docs/
└── adr/
├── 0001-use-postgresql-as-primary-database.md
├── 0002-adopt-typescript-for-frontend.md
├── 0003-use-redis-for-session-storage.md
├── 0004-keep-monolith-defer-microservices.md
└── README.md (index)
Pros:
✓ Version controlled with the code
✓ Lives where developers work
✓ Pull request workflow for new ADRs
✓ Links work (relative paths)
✓ Searchable via grep/IDE
Cons:
✗ Not accessible to non-developers
✗ Split across repos in multi-repo setup
OPTION 2: WIKI/NOTION/CONFLUENCE
════════════════════════════════════════════════════════════════════
Pros:
✓ More accessible to non-developers
✓ Better search
✓ Rich formatting
Cons:
✗ Separate from code (out of sight, out of mind)
✗ Version control is worse
✗ Links break more easily
OPTION 3: MONOREPO ADR FOLDER (for multi-repo teams)
════════════════════════════════════════════════════════════════════
One central repository for ADRs across all projects.
architecture/
├── adr/
│ ├── platform/
│ │ └── 0001-use-kubernetes.md
│ ├── frontend/
│ │ └── 0001-adopt-react.md
│ └── backend/
│ └── 0001-use-postgresql.md
└── README.md
Pros:
✓ Single source of truth
✓ Cross-cutting decisions in one place
Cons:
✗ Extra repo to maintain
✗ ADRs separate from relevant code
RECOMMENDATION:
───────────────
Start with in-repo ADRs. Simple, effective, discoverable.
Create a shared location only for cross-repo decisions.
The ADR Index
# Architecture Decision Records
This directory contains our Architecture Decision Records (ADRs).
## What is an ADR?
[Brief explanation and link to this guide]
## Index
| ID | Title | Status | Date |
|----|-------|--------|------|
| [ADR-001](0001-use-postgresql.md) | Use PostgreSQL as primary database | Accepted | 2023-06-15 |
| [ADR-002](0002-adopt-typescript.md) | Adopt TypeScript for frontend | Accepted | 2023-07-01 |
| [ADR-003](0003-redis-sessions.md) | Use Redis for session storage | Accepted | 2024-01-15 |
| [ADR-004](0004-keep-monolith.md) | Keep monolith, defer microservices | Accepted | 2024-01-20 |
| [ADR-005](0005-reject-graphql.md) | Reject GraphQL for public API | Rejected | 2024-02-01 |
## By Category
### Database
- [ADR-001](0001-use-postgresql.md): Use PostgreSQL as primary database
- [ADR-003](0003-redis-sessions.md): Use Redis for session storage
### Frontend
- [ADR-002](0002-adopt-typescript.md): Adopt TypeScript for frontend
### Architecture
- [ADR-004](0004-keep-monolith.md): Keep monolith, defer microservices
- [ADR-005](0005-reject-graphql.md): Reject GraphQL for public API
## Superseded Decisions
| Original | Superseded By | Summary |
|----------|---------------|---------|
| [ADR-002](0002-sessions-memory.md) | [ADR-003](0003-redis-sessions.md) | Sessions moved from in-memory to Redis |
## How to Contribute
[Link to process for proposing new ADRs]
Linking ADRs to Code
MAKING ADRS VISIBLE IN CONTEXT:
════════════════════════════════════════════════════════════════════
IN CODE COMMENTS:
────────────────
// Session storage configuration
// See ADR-003 for decision context: docs/adr/0003-redis-sessions.md
const redisConfig = {
host: process.env.REDIS_HOST,
// ...
};
IN COMMIT MESSAGES:
──────────────────
feat: implement Redis session storage
Replaces in-memory sessions with Redis cluster.
See ADR-003 for decision rationale.
IN PR DESCRIPTIONS:
──────────────────
## Summary
This PR implements session storage in Redis.
## Background
Per [ADR-003](../docs/adr/0003-redis-sessions.md), we're moving
from in-memory sessions to Redis to enable horizontal scaling.
IN README FILES:
───────────────
## Architecture
This service uses PostgreSQL for data storage. See
[ADR-001](docs/adr/0001-use-postgresql.md) for the decision context.
THE GOAL:
────────
When someone asks "why is this built this way?", the answer
should be one click away.
ADR Lifecycle
Writing New ADRs
ADR CREATION WORKFLOW:
════════════════════════════════════════════════════════════════════
1. RECOGNIZE THE DECISION
────────────────────────
"We're about to make an architectural choice."
"This will be hard to change later."
"We keep debating this—let's document a decision."
2. WRITE THE DRAFT
────────────────
Start with Context. Get the situation clear first.
Then Decision. Be direct.
Then Consequences. Be honest.
Optionally Alternatives.
3. REVIEW (Optional but recommended for significant decisions)
─────────────────────────────────────────────────────────────
• Share with relevant stakeholders
• Get input on accuracy
• Ensure decision is actually agreed
4. ACCEPT
──────
Mark status as "Accepted"
Add to index
Merge/commit
5. COMMUNICATE
───────────
Post to team channel: "New ADR: We're using Redis for sessions"
Include in relevant meetings
Reference in related PRs
Updating and Superseding
ADR IMMUTABILITY PRINCIPLE:
════════════════════════════════════════════════════════════════════
DON'T edit accepted ADRs (except typos/clarifications).
DO write new ADRs that supersede old ones.
Why? ADRs are historical records. The old context was real.
Editing it rewrites history and loses institutional memory.
WHEN DECISIONS CHANGE:
════════════════════════════════════════════════════════════════════
WRONG:
──────
Edit ADR-003 to change Redis → Memcached.
(Loses context of why we originally chose Redis)
RIGHT:
──────
1. Update ADR-003 status: "Superseded by ADR-015"
2. Write ADR-015: "Switch from Redis to Memcached"
3. ADR-015 Context explains what changed
ADR-015 might say:
──────────────────
"In ADR-003, we chose Redis for session storage. That decision
made sense at the time. However:
• We're now using Memcached for caching (added since ADR-003)
• Running both Redis and Memcached is operational overhead
• We don't use Redis features beyond basic key-value storage
We will consolidate on Memcached for sessions."
This preserves the history: "We chose Redis, then switched to
Memcached because we added Memcached for caching."
ADR Decay and Review
ADR HEALTH CHECK:
════════════════════════════════════════════════════════════════════
Quarterly (or during architecture reviews), ask:
1. ARE THERE MISSING ADRS?
─────────────────────────
"We made some big decisions last quarter. Are they documented?"
Look for:
• New technologies adopted
• Architectural changes
• Patterns established
2. ARE THERE OUTDATED ADRS?
─────────────────────────
"Have any decisions changed without updating the ADR?"
Look for:
• ADRs that don't match current reality
• Decisions that were reversed
• Context that's no longer true
3. ARE ADRS BEING REFERENCED?
───────────────────────────
"Are people actually using these?"
Check:
• Are new team members reading ADRs?
• Are ADRs referenced in discussions?
• Does the team know they exist?
IF ADRS ARE IGNORED:
════════════════════════════════════════════════════════════════════
Ask why:
• Hard to find? → Improve discoverability
• Too long? → Shorten, add summaries
• Outdated? → Update or supersede
• Not valuable? → Are you ADR'ing the right things?
• Cultural issue? → Leadership needs to model ADR usage
Making ADRs Engaging
ADRs that get read are ADRs that are well-written.
Write Like a Human
CORPORATE SPEAK VS HUMAN SPEAK:
════════════════════════════════════════════════════════════════════
BEFORE (Corporate):
───────────────────
"Subsequent to a comprehensive evaluation of available database
management solutions, taking into consideration factors including
but not limited to scalability requirements, operational
complexity, and alignment with organizational competencies, the
technical committee has determined that the PostgreSQL relational
database management system represents the optimal selection for
our data persistence layer."
Translation: "We picked PostgreSQL."
AFTER (Human):
──────────────
"We evaluated several databases for storing our customer data.
PostgreSQL won because:
1. Our data is relational (orders have line items, items have products)
2. The team knows PostgreSQL well
3. It handles our current scale with room to grow
4. AWS RDS makes operations manageable
MongoDB was a close second but our data really is relational,
and none of us have MongoDB experience."
THE DIFFERENCE:
───────────────
• Short sentences
• Active voice
• Specific examples
• Honest about tradeoffs
• Readable in 60 seconds
Lead with the Decision
STRUCTURE FOR SCANNABILITY:
════════════════════════════════════════════════════════════════════
DON'T: Bury the lead
────────────────────
[3 paragraphs of context]
[2 paragraphs of evaluation criteria]
[1 paragraph of process description]
Finally: "Therefore, we chose PostgreSQL."
Busy readers give up before the decision.
DO: Lead with the answer
────────────────────────
## Decision
We will use PostgreSQL 15 on AWS RDS.
## Context
Here's why...
People can stop after the Decision if they just need to know what.
They can read Context if they want to know why.
Use Formatting Well
FORMATTING THAT HELPS:
════════════════════════════════════════════════════════════════════
HEADERS
───────
Break up sections. Allow skimming.
BULLET POINTS
─────────────
• Easier to scan than paragraphs
• Good for lists of requirements, consequences
• Keep each bullet to 1-2 lines
BOLD FOR EMPHASIS
─────────────────
Use **bold** for the key points you don't want missed.
"We will use **PostgreSQL**, not MongoDB."
TABLES FOR COMPARISON
─────────────────────
| Option | Pros | Cons |
|--------|------|------|
| PostgreSQL | Team knows it, ACID | Schema migrations |
| MongoDB | Flexible schema | No team experience |
CODE BLOCKS FOR TECHNICAL DETAILS
─────────────────────────────────
```sql
-- Row-level security pattern we'll use
CREATE POLICY tenant_isolation ON customers
USING (tenant_id = current_setting('app.tenant_id'));
CALLOUTS FOR IMPORTANT NOTES ────────────────────────────
Note: This decision assumes we stay on AWS. If we move to GCP, we should revisit RDS vs Cloud SQL.
---
## Common ADR Mistakes
┌─────────────────────────────────────────────────────────────────────┐ │ ADR ANTI-PATTERNS │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ THE RETROACTIVE JUSTIFICATION │ │ ───────────────────────────────────────────────────────────────── │ │ "We already built it with MongoDB. Write an ADR saying why." │ │ │ │ Problem: Rationalizing, not reasoning. People can tell. │ │ Fix: Be honest. "We chose X initially. Here's why. In retrospect, │ │ we'd still make the same choice because..." │ │ Or: "We chose X. In retrospect, Y might have been better. │ │ Here's why we're keeping X anyway." │ │ │ │ ───────────────────────────────────────────────────────────────── │ │ │ │ THE NOVEL │ │ ───────────────────────────────────────────────────────────────── │ │ 10-page ADR with exhaustive detail on every option. │ │ │ │ Problem: No one reads it. │ │ Fix: 1-2 pages max. Save details for design docs. │ │ If you can't summarize, you don't understand well enough. │ │ │ │ ───────────────────────────────────────────────────────────────── │ │ │ │ THE OBVIOUS DECISION │ │ ───────────────────────────────────────────────────────────────── │ │ ADR for using React in a React project. │ │ │ │ Problem: Noise. No one will reference this. │ │ Fix: Only ADR decisions that need justification. │ │ │ │ ───────────────────────────────────────────────────────────────── │ │ │ │ THE VAGUE DECISION │ │ ───────────────────────────────────────────────────────────────── │ │ "We will use a microservices architecture." │ │ │ │ Problem: Not verifiable. What counts as "microservices"? │ │ Fix: Be specific. "We will split the monolith into 3 services: │ │ user-service, order-service, and payment-service." │ │ │ │ ───────────────────────────────────────────────────────────────── │ │ │ │ THE ALL-POSITIVE CONSEQUENCES │ │ ───────────────────────────────────────────────────────────────── │ │ "This will improve performance, scalability, and maintainability."│ │ │ │ Problem: Reads like marketing. Every decision has tradeoffs. │ │ Fix: Be honest. "We get X, but we lose Y. We're accepting Z risk."│ │ │ │ ───────────────────────────────────────────────────────────────── │ │ │ │ THE ZOMBIE ADR │ │ ───────────────────────────────────────────────────────────────── │ │ ADR from 2020, still marked "Accepted", but we changed it in 2022.│ │ │ │ Problem: Misleads people. Destroys trust in ADRs. │ │ Fix: Mark superseded. Write new ADR for the new decision. │ │ │ │ ───────────────────────────────────────────────────────────────── │ │ │ │ THE MISSING CONTEXT │ │ ───────────────────────────────────────────────────────────────── │ │ "We chose PostgreSQL because it's the best database." │ │ │ │ Problem: Best for what? Under what constraints? │ │ Fix: Explain the specific situation, requirements, constraints. │ │ │ └─────────────────────────────────────────────────────────────────────┘
---
## Tooling
### Automation Options
ADR TOOLING: ════════════════════════════════════════════════════════════════════
adr-tools (bash) ──────────────── Classic CLI tool for managing ADRs.
Commands: adr new "Use PostgreSQL" # Create new ADR adr list # List all ADRs adr link 5 "Supersedes" 3 # Link ADRs
Install: brew install adr-tools Repo: https://github.com/npryce/adr-tools
log4brains (Node.js) ──────────────────── Modern tool with web UI for browsing ADRs.
Features: • Web interface for reading ADRs • Timeline view • Search • Markdown preview
Install: npx log4brains init Repo: https://github.com/thomvaill/log4brains
adr-viewer (Python) ─────────────────── Static site generator for ADR folders.
Features: • Creates HTML site from ADR markdown • Graph visualization of superseded ADRs
Install: pip install adr-viewer Repo: https://github.com/mrwilson/adr-viewer
Simple script (DIY) ───────────────────
create-adr.sh
#!/bin/bash NUMBER=$(ls docs/adr | wc -l | xargs) PADDED=$(printf "%04d" $NUMBER) SLUG=$(echo "$1" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') FILE="docs/adr/${PADDED}-${SLUG}.md"
cat > "$FILE" << EOF
ADR-${PADDED}: $1
Status: Proposed Date: $(date +%Y-%m-%d) Deciders: [Names]
Context
[What is the situation?]
Decision
We will...
Consequences
[What are the results?] EOF
echo "Created: $FILE"
RECOMMENDATION: ─────────────── Start with just markdown files and a simple script. Add tooling only if you outgrow that.
### IDE Integration
MAKING ADRS EASY TO ACCESS: ════════════════════════════════════════════════════════════════════
VS CODE SNIPPETS ──────────────── // .vscode/adr.code-snippets { "ADR Template": { "prefix": "adr", "body": [ "# ADR-${1:NNN}: ${2:Title}", "", "Status: Proposed", "Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}", "Deciders: ${3:Names}", "", "## Context", "", "${4:What is the situation?}", "", "## Decision", "", "We will ${5:...}", "", "## Consequences", "", "${6:What are the results?}" ] } }
QUICK ACCESS ──────────── Bookmark the ADR folder in your IDE. Add "adr" as a keyboard shortcut to open it.
SEARCH ────── Use IDE's global search to find ADRs by topic. "database" → finds ADR-001, ADR-015, ADR-023...
---
## Building ADR Culture
ADRs only work if people use them.
### Getting Buy-In
BUILDING ADR ADOPTION: ════════════════════════════════════════════════════════════════════
-
START WITH A REAL PROBLEM ────────────────────────── "Remember last month when we debated the caching approach for two hours, then again the next week because we forgot what we decided? ADRs prevent that."
Not: "We should have ADRs because it's best practice."
-
LEAD BY EXAMPLE ──────────────── Tech leads and senior engineers should write ADRs first. Others will follow when they see value.
-
REFERENCE THEM CONSTANTLY ────────────────────────── In discussions: "Let me check... ADR-012 says we decided X." In code review: "This aligns with ADR-007." In onboarding: "Read the ADRs to understand our architecture."
If you don't use them, no one will.
-
KEEP THEM SHORT ──────────────── Long ADRs don't get read. If people aren't reading them, they won't write them.
-
MAKE THEM EASY TO CREATE ───────────────────────── One command to create an ADR. Simple template. No approval process (for small teams).
-
CELEBRATE GOOD ADRS ──────────────────── "This ADR on authentication is really well-written." Share good examples with the team.
-
REVISIT IN RETROSPECTIVES ────────────────────────── "Did any past ADR predictions turn out wrong?" "Are there decisions we made that should have had ADRs?"
### Making It Stick
HABITS THAT BUILD ADR CULTURE: ════════════════════════════════════════════════════════════════════
DURING DESIGN DISCUSSIONS ───────────────────────── "Before we implement, let's write the ADR so we're aligned."
Use ADR as a forcing function for clear thinking.
AFTER DECISIONS ─────────────── "I'll create an ADR to capture what we decided."
Make ADR creation part of the decision process.
DURING ONBOARDING ───────────────── "Here's our ADR folder. Read these to understand why things are built the way they are."
ADRs become onboarding documentation.
DURING CODE REVIEW ────────────────── "This introduces a new pattern. Should we have an ADR?"
ADRs become a checkpoint for significant changes.
DURING INCIDENTS/DEBUGGING ────────────────────────── "Why is this built this way? Let me check the ADR."
ADRs answer the "why" questions.
DURING RETROSPECTIVES ───────────────────── "Our ADR said we accepted X risk. It happened. Should we reconsider the decision?"
ADRs become learning tools.
---
## Quick Reference
┌─────────────────────────────────────────────────────────────────────┐ │ ADR QUICK REFERENCE │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ WHEN TO ADR │ │ ───────────────────────────────────────────────────────────────── │ │ ✓ Technology choices │ │ ✓ Architecture patterns │ │ ✓ Cross-cutting decisions │ │ ✓ Expensive-to-reverse decisions │ │ ✓ Decisions NOT to do something │ │ ✗ Implementation details │ │ ✗ Easily reversible choices │ │ │ │ STRUCTURE (Keep it short!) │ │ ───────────────────────────────────────────────────────────────── │ │ Title: "ADR-NNN: Use X for Y" │ │ Status: Proposed | Accepted | Superseded | Deprecated │ │ Context: What's the situation? Why decide now? │ │ Decision: "We will..." (specific, verifiable) │ │ Consequences: Good, bad, and risks accepted │ │ Alternatives: What else we considered and why not │ │ │ │ WRITING TIPS │ │ ───────────────────────────────────────────────────────────────── │ │ • 1-2 pages max │ │ • Lead with the decision │ │ • Be specific, not vague │ │ • Include negative consequences │ │ • Write for someone with no context │ │ │ │ LIFECYCLE │ │ ───────────────────────────────────────────────────────────────── │ │ • Don't edit accepted ADRs (except typos) │ │ • Write new ADRs that supersede old ones │ │ • Mark superseded ADRs with links to new ones │ │ │ │ GETTING ADOPTION │ │ ───────────────────────────────────────────────────────────────── │ │ • Lead by example │ │ • Reference ADRs constantly │ │ • Keep them short │ │ • Make them easy to create │ │ • Include in onboarding │ │ │ └─────────────────────────────────────────────────────────────────────┘
---
## Conclusion
The goal of an ADR isn't documentation. It's decision-making.
A well-written ADR forces clear thinking: you can't write a good Context section without understanding the problem. You can't write a good Consequences section without honestly considering tradeoffs.
The written record is a side effect—a valuable one, but secondary to the clarity that comes from writing it.
ADRs that get read share these traits:
- **Short:** 1-2 pages, not 10
- **Clear:** Lead with the decision, explain the why
- **Honest:** Acknowledge tradeoffs and risks
- **Discoverable:** Easy to find, linked from code
- **Current:** Superseded when decisions change
- **Referenced:** Used in discussions, onboarding, code review
Start with your next significant decision. Write a one-page ADR. Share it with the team. Reference it when the decision comes up again.
Six months from now, when someone asks "why did we build it this way?", you'll have an answer—and so will everyone else.
What did you think?