Architecture decision records
Record the decision where the code lives, one short file per decision, written at the moment it is made. What makes it worth reading a year later is not the format but the context and the rejected alternatives — the parts nobody can reconstruct afterwards.
A good decision record reads like the note a surgeon writes after an operation. Not a textbook chapter — a page saying what was found, what was done, and what was ruled out, so whoever treats the patient next is not guessing.
A search for a template usually means somebody has already told you to start writing these, and left the shape of the file to you. Here it is, in full, before any argument for why it matters: one file per decision, five short fields, written when the decision is made rather than reconstructed later. What follows is not a case for documentation — it is which of those fields survive contact with a system a year on, and which ones nobody will ever come back to read.
What the file has to contain#
Five fields, and each one answers a question a specific future reader will actually ask.
| Field | What it is for |
|---|---|
| Decision | One sentence, active voice, naming what was chosen — “the API uses a single ordered queue per tenant,” not “several approaches to ordering were discussed.” |
| Context | What was true when the call was made: the constraint, the deadline, the thing already built that ruled out the tidy answer. This is the part that stops making sense the moment the constraints change, which is exactly why it has to be written down rather than left to memory. |
| Alternatives rejected | The payload. Nobody asks what was done a year later — the code already says that. They ask whether the obvious other option was considered, and this is the only place that question gets answered. |
| Consequences accepted | What got worse, stated plainly. A decision with no downside was not a decision — it was the only option, and did not need a record at all. |
| Status | Proposed, accepted, or superseded. It exists because a record describes a decision at a moment, not the system as it stands today — and superseding is the only way to change one without misrepresenting when something was actually believed. |
The status field carries more weight than it looks like. Without it, an old record and a current one read as equally authoritative, and a reader has no way to tell which decision is still in force.
That is the whole argument for treating the file as a record of a moment rather than a description of the system: the constraints that produced the decision were true once. Editing the context to match how the system works now is not maintenance — it is the failure mode this article comes back to.
Why they die#
Three habits kill them.
Written after the fact
Filled in once the code is already done, from memory, so the context is reconstructed rather than recorded — and reconstructed context quietly rewrites itself to justify the choice that was already made.
Written for nobody in particular
Aimed at an imagined future auditor instead of the next engineer touching this code, so it reads like a design document — formal, hedged, thorough — and formal documents are the ones nobody opens under deadline.
Written for every decision
Naming a variable is a decision too, in the loosest sense, and a directory that records every one of them buries the choices that were genuinely expensive to reverse under a pile nobody would ever revisit.
What keeps a set of records alive is not a policy, it is where the writing happens. Write the record in the same change that makes the decision, so it gets reviewed alongside the code instead of filed separately and forgotten. Keep it short enough to write in one sitting — length is what turns a record into homework. And link to it from the code it governs, so it is found by someone reading the thing it explains, not by someone browsing a directory that has stopped meaning anything to them.
The test for whether a decision earns one of these at all: is it expensive to reverse? A choice that costs an afternoon to undo does not need a record. A choice that other parts of the system will come to depend on does — not because it feels important on the day, but because reversing it later is exactly the cost this file exists to make visible in advance.
Idempotency keys and safe retries is a good example of the kind of decision this format exists for: expensive to reverse once callers already depend on the promise. This article is node seven on the architecture path.
The record that got rewritten#
The failure is not an empty directory. It is a full one that has quietly stopped being architecture decision records at all.
Somebody opens an old record and edits it — not out of carelessness, but out of a reasonable-looking instinct to keep things accurate. The context section gets rewritten to match how the system works now. The decision gets softened because it reads oddly next to the current code. Each edit looks like tidying. What it actually does is erase the one thing the record was for: an honest account of what was true when the call was made. After enough of these edits, the record of why describes a system that never existed at the moment the decision happened.
The symptoms are concrete and easy to spot once you know to look for them. A reader cannot tell, from the file alone, whether a given record is still the live decision or a historical one nobody retired. Two records describe the same part of the system and quietly disagree, because one was edited and the other was not. And the directory drifts into being a wiki with worse tooling and no search — a set of pages about the current state of things, competing with the actual documentation for that job and losing.
The fix is not a review policy. It is a rule stated plainly: an ADR is immutable by design, and editing one destroys the only property that made it worth writing in the first place. When a decision changes, the correct move is a new record that supersedes the old one, with a line pointing back to it — leaving both intact, and both honest about when they were true.
The format earns its keep narrowly: an ADR does not replace a design document, a runbook, or the comments in the code, and a team that writes one for every decision is writing them for nothing — the discipline is entirely in what gets left out. Reserve the format for the choices worth reversing carefully, and let everything else stay where it already lives.
IF YOU REMEMBER ONE THING
An ADR is not a description of the system. It is a record of what was believed, and why, at the moment a decision was made — write a new one when that changes, and leave the old one alone.
Questions people also ask
5 QUESTIONSWhere should ADRs live?
In the repository, next to the code the decision governs, so the change and the record are reviewed together and neither drifts out of sync with the other. A folder such as docs/decisions works; what matters is that it ships in the same repository, and ideally the same pull request, as the change it explains.
What happens when a decision changes?
The old record stays exactly as it was, marked superseded, with a line pointing to the record that replaces it. Editing the original to match the new situation destroys the one thing that made it worth having: an honest account of what was believed at the moment it was written.
How long should an ADR be?
Short enough to write and read in a single sitting — a page, not a chapter. Length is itself a symptom: a record that needs several pages to explain one decision is usually explaining several decisions at once, or restating a design document that already exists elsewhere.
Do ADRs replace design documents?
No. A design document describes how a system works; an ADR records why one specific choice was made, at the moment it was made. Runbooks, comments and design documents keep doing their own jobs — an ADR only earns its place for the choices expensive enough to need a reason on record.
Who should write the ADR?
Whoever made the call, while the context is still fresh enough to state honestly — usually the person, or the small group, who did the work, not a separate role assigned to document it afterwards. Context reconstructed after the fact is the first way these records go wrong.