ADRs in Practice: Architecture Decision Records That Teams Actually Read
Every engineering team has the same conversation at least once a year. “Why are we using Postgres and not MongoDB?” “Who decided we’d build our own auth?” “Didn’t we agree on gRPC two years ago?” Someone goes to Confluence, finds three partially-conflicting documents and a deprecated Slack thread, and the team rediscovers the context the hard way — usually after reversing a decision that was actually fine.
Architecture Decision Records (ADRs) are the antidote. A short, structured document for every significant decision: what we decided, why, what we considered, what the tradeoffs are. Committed to the repo alongside the code the decision affects. Immutable once accepted (new decisions supersede rather than edit). Read during onboarding. Referenced in code reviews.
Michael Nygard introduced the format in 2011 and it has quietly become one of the most widely adopted documentation practices in software engineering. But like most practices, the gap between “we’re doing ADRs” and “ADRs work here” is wide. This post is the practical guide: what makes ADRs actually useful, when to write one (and when not), templates that earn their structure, review processes that don’t collapse into theater, and tooling that helps without getting in the way.
What an ADR actually is
An ADR is a short document — typically 1–3 pages — that records one significant architectural decision. The essentials:
- Title describing the decision (not the problem — “Use Postgres for primary storage,” not “Database selection”).
- Status (proposed, accepted, superseded, deprecated).
- Context explaining the forces at play.
- Decision stating exactly what was decided.
- Consequences covering the resulting tradeoffs, both positive and negative.
The format is minimalist on purpose. If it’s too long, nobody writes it. If it’s too unstructured, nobody reads it. The structure is the discipline; the shortness is the adoption strategy.
A canonical example, stripped to its bones:
|
|
That’s the whole thing. A developer joining the team in 2027 can read this, understand why, and decide whether changed circumstances warrant revisiting.
Why ADRs work
The value isn’t the documents. It’s the discipline of writing them. Several forces:
They force explicit tradeoff analysis. Writing “Consequences” is uncomfortable. Listing the downsides of your preferred option, in a permanent document, requires acknowledging that you chose a tradeoff. Teams that write ADRs reliably discover that decisions they’d made confidently were less examined than they thought.
They externalize tribal knowledge. “We decided this because Alice pointed out that X” becomes knowledge the team owns rather than knowledge Alice owns. When Alice leaves, the reasoning survives.
They prevent accidental reversal. “Let’s switch to MongoDB” is a harder argument to have when ADR-0012 is committed to the repo. The reversal starts with “supersede ADR-0012 with a new ADR explaining why the decision has changed” — which forces the same discipline that would have caught the original weak argument.
They accelerate onboarding. New engineers ask “why do we do it this way?” ten times in their first month. ADRs answer most of them without pulling senior engineers into synchronous conversation.
They create a low-ceremony review point. A “should we do X?” conversation becomes “here’s a draft ADR for X; what’s missing?” The document focuses discussion. Decisions happen faster.
The teams that benefit most are ones that have burned themselves on undocumented decisions. The teams that resist ADRs most are ones that haven’t been around long enough to accumulate that pain.
Michael Nygard’s original template
Nygard’s 2011 template is still the most widely used:
# {Number}. {Title}
Date: {YYYY-MM-DD}
## Status
{Proposed | Accepted | Deprecated | Superseded by ADR-XXXX}
## Context
{Forces at play, technical, organizational, political, whatever
applies. Facts, not opinions, when possible.}
## Decision
{The decision in clear, active language. "We will use X" not "X
seems like a good option."}
## Consequences
{What becomes easier, what becomes harder, what was given up.
Include both positive and negative.}
Four sections. Readable in three minutes. No approval workflows, no stakeholder signoffs, no linking to a hundred other documents.
The simplicity is the point. Many teams add sections (alternatives considered, related decisions, links to discussions) and those are fine, but the core four are the minimum and often the maximum.
The “alternatives considered” expansion
A common and useful extension:
## Alternatives Considered
- **MongoDB**: rejected because we don't need flexible schema and
would lose the relational integrity critical to this domain.
- **DynamoDB**: rejected because the query patterns we'll need
require multiple secondary indexes, adding cost and complexity
exceeding RDS.
- **Self-hosted Postgres on EC2**: rejected because the operational
overhead doesn't match team capacity.
This section is the single most valuable thing a reader can see when revisiting a decision years later. “We considered MongoDB and rejected it because X” is vastly more useful than silence. The reader can judge whether X still holds.
Write it when alternatives were genuinely considered. Don’t manufacture alternatives for the form — a forced “other option” you never seriously evaluated looks obvious and undermines the rest of the document.
MADR: the slightly richer format
MADR (Markdown Architectural Decision Records) is a community template with more structure. A typical MADR doc:
# {Title}
- Status: {proposed | accepted | deprecated | superseded}
- Deciders: {people involved}
- Date: {YYYY-MM-DD}
## Context and Problem Statement
{2-3 sentences on the situation and question.}
## Decision Drivers
- {force 1}
- {force 2}
## Considered Options
- {option 1}
- {option 2}
## Decision Outcome
Chosen option: "{option}", because {rationale}.
### Positive Consequences
### Negative Consequences
## Pros and Cons of the Options
### {option 1}
{description}
- Good, because {argument}
- Bad, because {argument}
MADR is heavier but more structured. It works well for teams that want more rigor or that need ADRs to serve a broader audience (executives, compliance, regulators). It’s overkill for a small team making a hundred decisions a year.
My preference: start with Nygard, add an “Alternatives Considered” section when relevant, graduate to MADR only if the team explicitly wants it. The shorter format has higher completion rates.
When to write an ADR (and when not to)
The judgment call. Over-indexing either way kills the practice.
Write an ADR when the decision:
- Affects multiple teams or services.
- Is hard to reverse (changing primary database, authentication model, programming language, cloud provider).
- Involves a significant tradeoff that future engineers might question.
- Establishes a convention that’ll propagate across the codebase (directory structure, error handling pattern, API versioning scheme).
- Rejects a common or popular alternative — the “why not” question will be asked.
- Impacts deployment, operations, or security in durable ways.
Don’t write an ADR for:
- Reversible code decisions. Which sort algorithm to use in a function. What name to give a class. These are PR comments, not ADRs.
- Pure stylistic preferences (unless the team is formalizing a convention).
- Temporary tactical choices with a known expiration.
- Things that are adequately explained by a linked RFC or design doc — don’t duplicate.
The test I use: “Will someone, six months from now, ask why we did this and wish we’d written it down?” If yes, ADR. If no, don’t.
A mature team writes 1–3 ADRs per month. Fewer and you’re probably letting decisions drift undocumented; more and you’re probably writing things that don’t warrant the ceremony.
Storage and format
ADRs belong in version control, alongside the code they affect.
Location: typically docs/adr/ at the repo root, or /adr/ for a service. For a monorepo, per-service directories (e.g., services/payments/adr/) keep decisions close to the affected code.
Numbering: zero-padded sequential (0001-use-postgres.md, 0002-adopt-gateway-api.md). The number is permanent; even superseded ADRs keep their number. New decisions get new numbers.
Format: Markdown. Every IDE, every repo browser, every wiki renderer handles it. Use the filename as the title slug; put the actual title in the first H1.
Status transitions: edit the status line. An accepted ADR that later gets superseded updates its status to “Superseded by ADR-0047” but the rest of the content stays. The historical record should reflect the decision as it was made, not as it was revised.
Link to PRs and tickets in the context section when relevant. ADRs aren’t the only documentation; they’re the one that persists.
The review process
This is where most ADR practices fall apart.
Anti-pattern 1: no review. ADRs appear in the repo but nobody reads them. They document decisions already made unilaterally. The document serves archive purposes only; doesn’t shape the decision.
Anti-pattern 2: heavyweight approval. Every ADR requires sign-off from three senior engineers and two architects. Writing an ADR becomes a multi-week project. People stop proposing them.
Anti-pattern 3: rubber stamp. ADRs go through a mandatory review that never actually pushes back. Everyone approves everything. Theater.
A working middle ground:
- Author drafts an ADR with status “Proposed” in a PR.
- Reviewers comment on the PR like any other code review. Substantive engagement: is the context accurate? Are real alternatives considered? Are the consequences honest?
- Iteration as long as needed. Genuine disagreement should generate iterations; rubber-stamping shouldn’t.
- Merge with “Accepted” status when the review is satisfied. Reviewers should push back if the consequences section is suspiciously upbeat; if the author can’t name any downside, they haven’t thought hard enough.
The PR format keeps review light (it’s how you review everything) and visible (decisions are traceable via git history). Comment threads become part of the decision’s context — reviewers who want deeper discussion can link to them from the ADR.
Supersession and evolution
ADRs are not documents to update. They’re documents to add. When a decision changes:
- Write a new ADR explaining the new decision and why it supersedes the old one.
- Update the old ADR’s status to “Superseded by ADR-XXXX.”
- Don’t rewrite the old ADR’s content — the historical record matters.
# ADR-0012: Use Postgres as the primary operational database
## Status
Superseded by ADR-0047 (2027-08-01)
The old ADR still explains what was decided in 2026 and why; the new one explains what changed. A reader looking at the current state follows the chain.
Tooling
Several tools support the ADR workflow. Used sparingly, they help. Over-tooled, they become their own problem.
adr-tools (shell-based): the original. adr new "Use Postgres" creates a numbered file from a template. adr link records supersession. Zero-dependency, works anywhere.
|
|
Log4brains: generates a static site from ADRs in a repo. Nice index, search, timeline view. Good for teams with many ADRs that benefit from better navigation than a flat directory listing.
adr-viewer, adr-py, adr-lang-specific libraries: similar site-generation or linting utilities.
adr-manager (VS Code): edit ADRs with autocomplete, status tracking.
For most teams, the original adr-tools plus reading the Markdown directly is enough. Add a site generator once you have enough ADRs that browsing them has become annoying — usually 20+.
Naming conventions
A file at docs/adr/0012-use-postgres.md with # 12. Use Postgres as the primary operational database inside is the typical shape. Conventions that help:
- Declarative titles. “Use X,” “Adopt Y,” “Deprecate Z.” Not “Database Selection” or “Thoughts on Messaging.”
- Active voice in the decision. “We will use X” not “X is chosen” — clearer and forces accountability.
- Present tense throughout. The document describes the current state of the decision at the time of writing.
- One decision per ADR. Don’t bundle “use Postgres and RabbitMQ and gRPC” — each is a separate decision with separate tradeoffs.
If you find yourself writing “ADR-XX: Overview of our stack,” that’s not an ADR — that’s an architecture doc. Different genre; keep separate.
What belongs in “Context” vs “Consequences”
Commonly confused. The distinction:
- Context is the situation before the decision. Facts, constraints, requirements. The forces that shaped the decision. “We have 5 engineers, all of whom know Postgres. Our load is X. Our consistency requirements are Y.”
- Consequences are the situation after the decision. What’s better, what’s worse, what’s now locked in. “We benefit from RDS managed backups; we accept Amazon lock-in; we defer multi-region decisions.”
A reader reconstructs the decision from Context + Decision alone. Consequences explains what the team accepted in choosing. Both are essential.
Linking ADRs to code
A pattern that pays off: reference ADRs in the code that implements them.
|
|
Or, lighter, in module-level docstrings or README files. The purpose isn’t to document code; it’s to make ADRs findable when someone’s reading the code and wondering “why?”
Over-use this and comments become noise. Reserve for the few ADRs that directly shape the structure of the file or module.
ADRs at scale: cross-team decisions
For single-team ADRs, the PR workflow is simple. Cross-team decisions (e.g., “all services use JWT for internal auth”) are harder: who reviews? Who decides?
Patterns that work:
- Architecture guilds / principal engineer groups who own cross-cutting ADRs. Not gatekeepers; facilitators. They weigh in on ADRs that affect multiple teams.
- Tiered ADRs: service-level ADRs in the service repo; org-level ADRs in a shared repo. Different review audiences, same format.
- Explicit escalation paths: most ADRs are team-level; specific types (security-impacting, data-model-impacting, compliance-impacting) route through a specific approver.
The failure mode is “every ADR needs sign-off from twelve people.” The counter-failure is “every team picks everything independently; convergence never happens.” Tiering is the practical balance.
Reading ADRs
If your team already has ADRs, read them. Seriously. Set aside an afternoon during onboarding; make it part of the first-week ritual. Most teams write ADRs and never read them — which is nearly as bad as not having them.
Reading patterns:
- All of them, chronologically, during onboarding. Gives you the history of how the system got to its current shape.
- Relevant ones, contextually, when starting work on a feature. Before modifying the payments service, read the payments ADRs.
- Before proposing a reversal, always. If you’re about to propose switching away from Postgres, read ADR-0012 first.
The ADRs that go unread over years become archive rather than living documentation. Reading sessions — someone presenting an ADR at a team meeting, a “read three ADRs a week” habit — keep them alive.
Common pitfalls
A few ways ADR practices rot:
The ADR that isn’t actually a decision. “ADR-0023: Considerations for Database Selection” — and then no decision section. Or “we’ll evaluate options as we go.” An ADR without a decision isn’t an ADR; it’s a note.
Vague context. “We need a database.” What kind of data? What scale? What consistency? Without context, the decision can’t be evaluated later.
Optimistic consequences. “Consequences: faster queries.” Every consequences section should have negatives. If there are no negatives, you haven’t thought about it enough — or you’ve picked an option so dominant there was no real decision to make.
ADR sprawl without supersession. Ten ADRs on the same topic, none superseding the others. Reader has to figure out which one is current.
Writing ADRs after the fact for decisions already made. Fine occasionally; corrosive as a habit. The discipline of writing the ADR before the decision is what produces the benefits. Backfilled ADRs feel defensive and tend to be written to justify rather than to analyze.
Post-mortem as ADR. Incidents and their learnings belong in post-mortems, not ADRs. ADRs are for forward decisions. Post-mortem findings can trigger new ADRs, but the post-mortem itself is a separate artifact.
Adopting ADRs on an existing team
For teams considering introducing ADRs:
- Start with one. A current decision you’re actively making. Write the ADR; use it as the basis for team discussion; decide; merge.
- Introduce the format informally. Don’t open with a process document — “we should use ADRs and here’s our template and our review process.” Show, don’t tell. Write the first few yourself and invite commentary.
- Keep it lightweight. No mandatory reviews, no approval workflows initially. Just “if you make a significant decision, write one.”
- Backfill a handful. Pick 5–10 already-made decisions that keep getting questioned and write ADRs for them. Immediate value; encourages the habit.
- Review quality at quarter-end. Are the ADRs useful? Are people reading them? What would make them better?
- Only add process when it solves a problem. If decisions are being rubber-stamped, introduce a reviewer requirement. If ADRs are being skipped, discuss at retros. Adjust based on actual failure modes.
Most ADR practices either die from over-engineering (too much process) or neglect (no discipline). The sweet spot is “write them because they’re useful” — which requires the team to experience the usefulness. Start small, iterate, and let adoption grow organically.
The bigger picture
ADRs are one form of written decision-making. Richer alternatives exist — RFCs for larger proposals (Rust, Python, internal RFCs at many companies), design docs (Google’s widely-imitated format), product-style specifications for cross-functional decisions. ADRs complement these: quick, structured, low-ceremony, per-decision.
The underlying practice — forcing decisions to be articulated, considered, and recorded — is what actually matters. ADRs are a vehicle. Teams that write them well write less of their decision-making than teams with heavyweight RFC processes, and produce better outcomes because the documents are actually read.
Start writing them. Keep them short. Make the consequences honest. Read the old ones. Supersede when needed. A year in, you’ll wonder how you worked without them.
Comments