Architecture Decision Records: A Practical Guide for Engineering Teams
Six months into a project, an engineer asks why the codebase uses Postgres instead of MySQL. Nobody on the call knows. The original team has moved on, the Slack thread is lost, and the ticket that triggered the choice has been archived. So the team relitigates the decision from scratch, badly, because the original constraints are gone.
This is the gap that architecture decision records (ADRs) fill. They are short, dated, immutable documents that capture a single significant decision and the context around it. Ten minutes to write, indefinitely useful to read.
What an ADR Actually Is
An ADR is not a design doc. It is not an RFC. It is the small, durable artefact that sits between an engineering choice and the code that resulted from it.
The format was popularised by Michael Nygard’s 2011 post on documenting architecture decisions ↗. He proposed five sections: title, status, context, decision, and consequences. That core has barely changed in fifteen years because it works.
The point is the trade-off, not the choice. A future engineer reading an ADR should walk away knowing what was on the table and why one option won. If your ADR reads like a press release, you are doing it wrong.
The Minimum Viable ADR
Here is the structure I use on every project:
# 0007. Use Postgres for the primary data store
Date: 2026-04-29
Status: Accepted
## Context
We need a relational database for the core domain. The team has
operational experience with Postgres and MySQL. We expect heavy use
of JSONB for the audit log feature.
## Decision
Use Postgres 16, hosted on AWS RDS, single-region to start.
## Alternatives considered
- MySQL 8: weaker JSONB support, team less experienced with it
- DynamoDB: rejected, query patterns are too relational
- SQLite: fine for development, will not scale for production load
## Consequences
+ Strong JSONB support for the audit log
+ Familiar operational story for the on-call team
- Vendor lock to AWS RDS in the short term
- Cross-region replication is a future cost we have accepted
That is roughly 150 words. Anyone joining the team in 2027 can read it in 90 seconds and understand why the system looks the way it does.
Why Teams Skip ADRs (And Why That Costs Them)
Most teams I have worked with either do not write ADRs or wrote a few once and stopped. The reasons are predictable.
“We do not have time.” The full ADR template above takes ten minutes to write. The Slack debate that produced the decision took two hours. The cost is rounding error.
“We will remember why we did it.” You will not. Andrew Harmel-Law’s writing on conversational architecture ↗ is sharp on this point: organisational memory is fragile, and ADRs are one of the cheapest ways to preserve it.
“It feels like bureaucracy.” It feels like bureaucracy when you mandate ADRs for every code change. It does not when you reserve them for decisions that would be expensive to reverse.
The real cost of skipping ADRs shows up later, when an engineer spends a day excavating a choice that could have been read in a minute, or when a team accidentally rebuilds a system around constraints that no longer exist.
When to Write One
Not every decision deserves an ADR. The rough heuristic: if undoing this would take more than a week of engineering time, write one.
Concrete triggers:
- Choosing a database, message broker, or cache
- Picking an authentication or authorisation model
- Adopting (or replacing) a framework
- Defining service boundaries in a microservices migration
- Committing to a deployment topology (single-region vs multi-region, blue-green vs canary)
- Introducing a new language or runtime to the stack
- Deliberately accepting a build-or-buy trade-off
If the decision will not survive the next sprint, skip the ADR. Notes in the PR description are enough.
The ADR Lifecycle
ADRs are immutable, but decisions are not. The way you reconcile this is with status transitions and links between records.
When a new decision overrides an old one, you write a new ADR (status: accepted) that references the old one, then update the old ADR’s status to superseded with a link to the replacement. The old file stays in the repo. The history is the point.
Status Values Worth Using
- Proposed. Drafted, under review, not yet committed to.
- Accepted. The team is acting on this.
- Superseded. A later ADR replaces this one.
- Deprecated. No replacement, the decision simply no longer applies.
That is enough. Some teams add “rejected” for ADRs that lost a vote, which is useful when you want a paper trail of options that were seriously considered.
Storing ADRs and Tooling
Keep ADRs in the repo, under docs/adr/, numbered sequentially. Plain markdown. No special build pipeline.
If you want a small amount of automation, npryce/adr-tools ↗ is a tiny shell utility that scaffolds new ADRs and manages supersedes links. MADR ↗ (Markdown Any Decision Records) offers a slightly richer template if you want fields for the decision drivers and pros/cons table.
Whichever flavour you pick, the rules are the same:
| Rule | Why it matters |
|---|---|
| One decision per record | A record that covers three decisions is a design doc in disguise |
| Numbered, never renumbered | Stable IDs let other ADRs and tickets link reliably |
| Immutable once accepted | If the decision changes, write a new ADR |
| Plain markdown in the repo | Discoverable through grep, reviewable in pull requests |
| Linked from related code | A code comment with the ADR number pays for itself the first time someone asks why |
Common Failure Modes
A few patterns that turn ADRs into noise:
Writing them after the fact, in bulk. Backfilling ADRs for decisions made years ago tends to produce sanitised history. Start with the next decision, not the last fifty.
Treating them as approval gates. ADRs document decisions, they do not authorise them. If your process needs review and sign-off, do that in an RFC or pull request and let the ADR record the outcome.
Padding them. A 2,000-word ADR is a design doc. Cut it back. The sections are titles, not invitations to write essays.
Letting them rot. ADRs whose status was never updated when the decision changed are worse than no ADRs, because they actively mislead. A quarterly sweep to mark superseded records is enough.
This connects to a wider point about engineering maturity. Senior engineers, in the sense I have written about before, are the ones who reach for tools like ADRs because they have felt the cost of not having them. The same instinct that drives a careful approach to legacy code archaeology drives the desire to leave a record for whoever comes next.
The ThoughtWorks Tech Radar has recommended lightweight ADRs since 2017 ↗, and the reasoning is unchanged: the cost of writing one is trivial, the value of having one is durable, and the cost of not having one compounds quietly.
Start With the Next Decision
If your team does not write ADRs today, do not draft a policy. Pick the next significant choice the team makes, write a 200-word record, drop it in docs/adr/0001-something.md, and link to it from the pull request. That single example will do more to spread the practice than any document you could write about why ADRs matter.
The first one is the only one that needs effort. After that, the format does the work.
Frequently asked questions
What is an architecture decision record?
An architecture decision record (ADR) is a short document that captures a single significant technical decision, its context, the options that were considered, and the consequences. ADRs live in version control alongside the code, so the reasoning behind a system is recorded where engineers can find it. Most ADRs are between 200 and 500 words.
What should I include in an ADR?
A useful ADR has at least four sections: context (what forced the decision), decision (what you chose), alternatives (what you rejected and why), and consequences (the trade-offs you accepted). Many teams add a status field (proposed, accepted, superseded) and links to related ADRs. Skip implementation detail, that belongs in code or runbooks.
How are ADRs different from RFCs or design docs?
RFCs and design docs propose work and gather feedback before a decision. ADRs record the decision after it has been made. The two complement each other: a long RFC can produce a short ADR that captures the agreed outcome. Design docs describe how something will be built; ADRs explain why a particular approach won.
How do I introduce ADRs to a team that does not use them?
Start by writing one ADR for the next significant decision the team makes, store it in the repo under docs/adr/, and link to it from the pull request. Do not retrofit ADRs for every past decision. Once the team sees the value of having context attached to decisions, ADRs will spread naturally.
Should ADRs ever be deleted?
No. An ADR is an immutable record of what the team thought and chose at a point in time. When a decision changes, write a new ADR with status accepted that references the old one and mark the old one as superseded. Deleting ADRs erases the history that makes the format useful.
Enjoyed this article? Get more developer tips straight to your inbox.
Comments
Join the conversation. Share your experience or ask a question below.
No comments yet. Be the first to share your thoughts.