Architecture decision records, a practical guide
How to write architecture decision records your team will actually maintain, including a copyable template and a worked example you can paste into your repo today.
Six months after the billing service shipped, a new engineer notices it talks to Postgres while every other service on the team uses DynamoDB. It looks like a mistake. She asks in the team channel why billing is special, and gets three half-answers and a shrug. The person who made the call left the company in March. There is no ticket, no comment in the code, no note in the wiki. The decision was made carefully, with real tradeoffs weighed in a meeting, and all of that reasoning evaporated the moment the meeting ended. What is left is a strange-looking choice and nobody who can defend it.
That gap between a deliberate decision and the missing record of why is exactly what an architecture decision record fixes. An ADR is a short document that captures one architectural decision, the context that forced it, and the consequences the team accepted by making it. It is one of the cheapest practices a team can adopt, and one of the most commonly abandoned. This guide covers what an ADR actually is, a template you can copy, where ADRs should live, and the specific habit that keeps them from rotting.
What an ADR is, precisely
An architecture decision record documents a single significant decision at the moment it was made. Significant means it was hard to reverse, affected more than one team, or constrained future work. "We chose Postgres for billing" is an ADR. "We renamed a variable" is not.
The defining property of an ADR is that it is immutable. Once a decision is accepted and recorded, you do not edit the record to reflect new thinking. The record describes what the team knew and decided at one point in time, and that snapshot has value precisely because it does not change. If the decision is later reversed, you write a new ADR that supersedes the old one. The old one stays in the repo, wrong but honest, a true account of what people believed in March.
This is what separates an ADR from the documents it gets confused with.
An ADR is not a design doc. A design doc explores a problem space before you commit, weighs many options, and often lives for the duration of a project. An ADR is the much shorter artifact that records the one decision a design doc arrives at. You might link to the design doc from the ADR, but the ADR outlives it.
An ADR is not a ticket. A Jira ticket tracks work to be done and gets closed. It captures the task, not the reasoning, and it disappears into a backlog nobody reads two years later.
An ADR is not a wiki page that gets edited forever. The wiki page is the failure mode this whole practice exists to avoid. A page that anyone can edit at any time has no point-in-time meaning. You cannot tell what was true when, and you cannot tell who decided what. The immutability is the feature, and a wiki actively works against it.
A template you can copy
Here is a template that has survived contact with real teams. It is deliberately short. The whole document should fit on one screen, because a template that demands an hour of writing is a template nobody fills in.
# ADR-NNNN: Short title of the decision
## Status
Proposed | Accepted | Superseded by ADR-XXXX
## Context
What forces are at play? What is the problem, the constraints, the
requirements, and the things you cannot change? State the facts that
made a decision necessary. Avoid arguing for the answer here. Just
describe the situation honestly, including pressures that point in
different directions.
## Decision
The decision, stated in one or two plain sentences. Active voice:
"We will use X." Not "X should probably be used."
## Consequences
What becomes easier and what becomes harder as a result. Include the
costs you are accepting, not just the benefits. This section is what
the future reader needs most.
## Alternatives considered
The other options you seriously weighed and why you did not pick them.
One or two sentences each. This is what stops someone from reopening
the debate from scratch in a year.
The Context, Decision, and Consequences sections come from Michael Nygard's original 2011 ADR format, which is still the version most teams use. Status and Alternatives considered are the two additions worth keeping. Status gives the record a lifecycle. Alternatives considered is the section that saves you from relitigating the same argument every time a new hire wonders why you did not use the obvious thing.
Here is the template filled in for a real-feeling decision.
# ADR-0007: Use Postgres instead of DynamoDB for the billing service
## Status
Accepted
## Context
The rest of our platform runs on DynamoDB, and our team knows it well.
Billing has different needs. Invoices, line items, credits, and tax
calculations involve multi-row transactions and ad hoc reporting
queries that finance runs at month end. We need strong consistency
across related writes (an invoice and its line items must commit
together) and the ability to run relational queries we cannot predict
in advance. Modeling transactional, highly relational billing data in
a single-table DynamoDB design is possible but costly to build and
costly to change. Finance has also asked for direct SQL access for
audits, which DynamoDB does not offer.
## Decision
We will use Postgres (via RDS) as the primary datastore for the
billing service. Billing will own its own database and will not share
tables with other services.
## Consequences
We gain ACID transactions across related billing writes, predictable
relational queries, and SQL access for finance and audit. We accept a
second database technology in our stack, which means a second set of
operational runbooks, backups, and on-call knowledge. The billing
team takes ownership of Postgres tuning and migrations. Cross-service
queries that join billing data with DynamoDB-backed services are not
possible at the database layer and must go through service APIs.
## Alternatives considered
Single-table DynamoDB design: rejected because the relational
reporting queries finance needs are hard to model up front and
expensive to change later, and DynamoDB offers no direct SQL access.
Aurora Serverless: a reasonable fit, deferred because standard RDS
Postgres is simpler to reason about for our current scale, and we can
revisit if billing load grows unpredictably.
Notice what the example does. The Context does not argue for Postgres. It lays out the forces, including the real pull toward DynamoDB (the team already knows it), so a future reader can see the decision was not made out of ignorance. The Consequences section is honest about the cost: a second database to operate. That honesty is what makes the record trustworthy. Anyone can find this file, read it in two minutes, and understand why billing is the odd one out, without paging anyone.
Where ADRs live and how they connect to diagrams
Keep ADRs in the repo, not in a wiki. A directory like /docs/adr/ with one Markdown file per decision, numbered sequentially: 0001-record-architecture-decisions.md, 0002-use-grpc-for-internal-rpc.md, and so on. The number is a stable identifier you can reference from code comments, pull requests, and other ADRs.
Putting them in the repo gets you three things for free. They travel with the code, so they are there when someone clones the project. They go through code review, so a decision record gets the same scrutiny as a code change before it lands. And they are versioned, so you can see when a decision was recorded and by whom.
ADRs and diagrams answer different questions, and a good architecture overview uses both. A diagram shows the what: which services exist, how they connect, where data flows. An ADR explains the why: the reason billing uses Postgres, the reason two services were split apart, the reason you are locked into a particular vendor. The diagram tells you the billing service has its own Postgres database. ADR-0007 tells you why, and what you gave up to get it. Link them. The architecture overview page should point at the diagram and list the ADRs that explain the non-obvious parts of it. This is the same failure mode at the heart of docs rot: the picture keeps the structure and drops the reasoning behind it. ADRs are the half of the documentation that captures intent, which is the half a diagram cannot hold.
The common failure, and how to avoid it
The usual story is this. A team adopts ADRs during a big process push. Everyone is enthusiastic. They write fifteen records in two weeks, mostly backfilling decisions from the past year. Then the process push ends, the next deadline arrives, and the directory goes quiet. Six months later there are fifteen ADRs covering the period of enthusiasm and nothing covering the period since, which is the period people actually need. The practice did not fail because ADRs are bad. It failed because the team treated them as a project instead of a habit. And the abandoned directory is not free either: every decision that goes unrecorded becomes another piece of the real cost of stale documentation, paid later by whoever has to reverse-engineer the reasoning from the code.
A few specific rules keep ADRs alive.
Keep them cheap. One screen, fifteen minutes, written when the decision is fresh. The moment an ADR feels like a deliverable, people stop writing them. The template above is short on purpose. Resist the urge to add required sections.
One decision per record. Do not write a single ADR that covers "the billing architecture." Write one for the datastore, one for the queue, one for the API style. Small records are easier to write, easier to find, and easier to supersede later when one of those decisions changes and the others do not.
Use the status lifecycle. A record starts as Proposed when you open the pull request, so reviewers can argue with it before it lands. It becomes Accepted when the decision is final. It becomes Superseded when a later ADR replaces it. Those three states are enough.
Never edit an accepted ADR. This is the rule that protects the whole practice. When the team later decides to move billing off Postgres, you do not go back and rewrite ADR-0007. You write ADR-0021, "Move billing to Aurora," set its status to Accepted, and change ADR-0007's status to "Superseded by ADR-0021." Now the history is intact. A reader can follow the chain and understand not just where you ended up but how the thinking moved. Editing the old record in place destroys exactly the thing that made it worth writing.
Write the ADR in the pull request that implements the decision. This is the single habit that turns ADRs from a project into a practice. If the rule is "the PR that introduces a significant architectural change includes its ADR," then the record gets written while the reasoning is fresh, gets reviewed alongside the code, and never becomes a separate chore that competes with real work. The decision and its record ship together or not at all.
ADRs alongside living diagrams in ArchDocs
ArchDocs is being built so the why and the what live next to each other instead of drifting apart. The diagrams are living documents you edit in place rather than redraw, and the intent is for decision records to sit alongside them, so the box on the diagram and the reasoning behind it are one click apart rather than scattered across a repo and a wiki. The goal is to keep both halves honest at once: the structure stays current because the diagram is editable, and the reasoning stays attached because the record travels with it. ArchDocs is pre-launch, so this is the design and the intent, not a track record. The argument for the approach is the reasoning above, not adoption numbers we do not have. If you want the longer version of why the structural half rots in the first place, the flagship piece on why your architecture diagrams are lying to you makes the case.
Start with ADR-0001
The first ADR to write is the one that records that you are going to write ADRs. It sounds circular, and it is the most useful one in the directory. ADR-0001, "Record architecture decisions," states that the team will keep decision records in /docs/adr/, names the template, and sets the rule that significant changes ship with their ADR. It takes ten minutes and it gives everyone who joins later a precedent to point at. Write that file this week, then write the next ADR in the next pull request that changes something a future engineer will squint at. The billing service that nobody can explain is not the result of a bad decision. It is the result of a good decision with no record, and the fix is one short file written while the reasoning is still in someone's head.
Architecture docs that stay alive
ArchDocs keeps diagrams and docs in sync with your systems instead of letting them rot. Create a free account to start documenting your architecture.