Architecture Decision Records
Context, decision, alternatives, consequences — written once, never edited, superseded when it changes. The only document that cannot decay, because it describes the past.
The requirement, the obvious build, and why it breaks
Every lesson starts where the work starts: someone asked for something, and the first implementation that comes to mind survives until the requirement changes.
Which decisions deserve a written record, and what does that record need to contain to still be useful in three years?
For the third time in four years, someone proposes replacing the message queue with the database. Nobody can remember what was wrong with that idea, so it gets a prototype and two weeks before somebody rediscovers the reason.
Important decisions get discussed in a design document and agreed in a meeting. That is enough — the decision is in the code afterwards, and the design doc is in the drive if anyone needs it.
The code shows what was decided and never what was rejected. "We use a queue" is visible; "we tried the database and it could not give us ordering per customer" is not, and only the second one prevents the re-proposal.
- The code shows what was decided and never what was rejected. "We use a queue" is visible; "we tried the database and it could not give us ordering per customer" is not, and only the second one prevents the re-proposal.
- A design document is edited until agreement, so it ends up describing the final proposal and not the reasoning path. The alternatives that were dismissed in the second meeting simply vanish from it.
- As people leave, the decision becomes folklore: a rule the team follows without knowing why, which is both cargo cult and unrevisable — nobody can argue against a reason nobody can state.
- Meanwhile the conditions change. Without a written record of what the decision assumed, there is no way to notice that the assumption stopped holding, so a good decision quietly becomes a bad one.
What limits the solution, and what must never stop being true
This domain leads with these two. A design that ignores its constraints is not a design, and an invariant nobody named is one nothing is protecting.
- It has to be cheap enough to write on the day of the decision, or it will only ever be written for decisions important enough to have a project, which are the ones already documented elsewhere.
- It has to be readable by someone who was not present, does not know the people involved, and cannot ask them.
- It has to remain useful after the decision is reversed, because the reasoning behind a reversed decision is exactly what a later reader needs (Revisit Triggers).
- An ADR is never edited after it is accepted. It is a statement about what was known and decided on a date, and editing it destroys the property that makes it trustworthy (Documentation Decay).
- A superseded record stays in place. Deleting it removes the reason the current decision exists.
Who owns what, and where the seams fall
Responsibilities decide boundaries; boundaries decide what an interface has to say.
- The person making the decision owns writing it down, that day, in fifteen minutes. Delegating it to a technical writer later loses the alternatives, which is the part only they remember.
- The record owns the reasoning, not the design. Implementation detail belongs in the code; the ADR holds why this rather than that.
- A later record owns superseding an earlier one. Nobody owns keeping an ADR up to date, because that is not a thing an ADR can be (Decision Records).
- ADRs live in the repository whose code they constrain, numbered and immutable. A decision affecting several repositories goes in the one that owns the constraint, and is linked from the others (Docs Close to Code).
- The boundary between an ADR and a design doc is time: a design doc argues before the decision and is edited; an ADR records after it and is not.
- The boundary between an ADR and a comment is scope: if it constrains one function, it is a comment; if it constrains how the team will work for the next two years, it is a record (Comments).
A whole record, and it should stay this short
The template is four sections and a trigger. What makes it work is not the structure but the discipline of writing the alternatives with their actual reasons, which is the part that takes real effort and the part that pays.
Read it as a future engineer would: the question is whether you could argue with it, and whether you could tell that it no longer applies.
1# 014. Use a queue for outbound webhooks2 3Status Accepted, 2024-03-11. Supersedes nothing.4 5Context ~2k webhooks/min, spiky. Customers require per-customer6 ordering. Two engineers own delivery. Postgres 14 already7 in production; no queue operated today.8 9Decision Deliver webhooks through a dedicated queue with one10 partition key per customer. Delivery state lives in the11 queue, not in the orders table.12 13Alternatives14 Postgres table + poller Simplest, no new infrastructure. Rejected:15 per-customer ordering needs SELECT FOR16 UPDATE SKIP LOCKED across a hot table, and17 we measured lock contention at 5x current18 peak in a load test.19 Synchronous delivery Rejected: a slow customer endpoint would20 block the request that produced the event.21 Managed vendor Rejected on data residency: EU customer22 payloads may not leave the region under23 contract 4.2.24 25Consequences26 + Ordering guarantee is the queue's, not ours.27 + Retry and DLQ behaviour is operational, not application code.28 - A second datastore to operate, back up and monitor. On-call29 surface grows for a two-person team.30 - Local development now needs the queue running.31 - Delivery state is no longer joinable with orders in SQL.32 33Revisit If sustained volume drops below ~200/min, or if per-customer34 ordering stops being a contractual requirement.The alternatives section is doing the work: each rejection has a specific reason a reader can check, and one of them cites a measurement rather than an opinion. The consequences include three costs, which is what makes the whole record credible (The Trade-off Matrix).
Which decisions are worth fifteen minutes
Most decisions are not. The practice dies when it is applied to everything, so the selection criterion matters as much as the template.
The test is a conjunction: is it expensive to reverse, and is the reasoning non-obvious to someone reading only the code? Both must be true, and the second one eliminates most candidates.
Is it expensive to reverse, and is the reason invisible in the code?
when A datastore choice, a data format, a boundary between two teams, a rejected library everyone will suggest again.
cost Write it. Fifteen minutes now against a recurring rediscovery that costs weeks each time (Reversible and Irreversible Decisions).
when Using the company standard framework because it is the company standard.
cost One line in the README is enough. A record here adds volume without adding information (Types of Documentation).
when A local convention, an unusual algorithm choice inside one module.
cost A comment beside the code, where the person who needs it is standing (Comments).
when The same idea returns every year and gets prototyped again.
cost Write it retroactively, dated today, stating what is known now. A late record is worth much more than no record, provided it does not pretend to have been written then.
when The team is drifting and someone wants to document the drift as though it were chosen.
cost Do not write an ADR. Make the decision first — writing the record is often what reveals that nobody has (Design Review).
Where the record could live instead
ADRs are not the only place reasoning can go, and the alternatives are not absurd — a long commit message is genuinely durable, and a wiki page is genuinely more discoverable for non-engineers. What differs is how each survives time, tooling and turnover.
| Option | Simplicity | Operational | Migration cost | Note |
|---|---|---|---|---|
| ADR in the repository | Versioned with the code, reviewed like code, survives every tool migration because it is a text file. Invisible to anyone who does not read the repository. | |||
| Wiki page | Easiest to write and by far the most discoverable for non-engineers. Editable in place, so the history is lost, and it does not survive the next wiki migration. | |||
| Long commit message | Free, permanent and attached to the exact change. Findable only if you already know which commit to look at, which the reader usually does not. | |||
| Ticket or PR description | Where the discussion already happened. Tied to a vendor, often behind a licence seat, and the reasoning is buried in a thread rather than stated. | |||
| Nothing | Free today. Pays for itself in re-proposals, and the cost lands on people who were not in the room. |
caveat The migration axis here means survival of the record itself — through a wiki replacement, a ticket-system change or a repository split — rather than data migration in the usual sense. The scores also cannot express the thing that decides in practice: whether your team will actually write the record at all. A wiki page that gets written beats an ADR that does not, and any honest comparison has to start from what the team has demonstrated it will sustain.
How to build it
Most important first.
- Context — what was true when the decision was made: the constraints, the team, the load, the deadline. This is what lets a future reader judge whether it still applies, and it is the section that makes the record durable.
- Decision — one paragraph, in the active voice, stating what will be done. If it takes more than a paragraph it is probably several decisions and deserves several records.
- Alternatives — what else was considered and the specific reason each was not chosen. This is the section that gets skipped and the section with nearly all the value.
- Consequences — including the bad ones. A record listing only benefits is advocacy, and a future reader will correctly discount all of it (The Trade-off Matrix).
- Add a revisit trigger: the observable condition that should cause someone to reopen this. Without it the record is history; with it, it is a decision with an expiry (Revisit Triggers).
- Keep it to a page. The length limit is what makes them get written, and a longer document has a lower chance of being read by the person who needs it.
What the next change costs
The field this whole domain exists for. A structure is only better if it makes the change after this one cheaper — and it is worth saying which changes it does not help.
- With a record, revisiting the decision costs reading one page: the context is stated, so a reader can check which assumptions have changed and argue about those rather than starting from nothing.
- Without one, revisiting costs a prototype and a rediscovery — in the running example, two weeks, repeated roughly every eighteen months, forever.
- Writing one costs fifteen minutes at the moment of decision, when the alternatives are still in someone's head. The same fifteen minutes six months later costs an afternoon and produces a worse record.
- Fifteen minutes per decision is a real tax on a team that makes decisions daily, and most of those decisions will never be revisited — the value is concentrated in a minority you cannot identify in advance.
- Immutability means the collection contains records that are wrong about the current system, and a reader who does not check the supersession chain can be misled by one. The index has to make status obvious.
- Writing down rejected alternatives can entrench them as rejected, so a genuinely improved version of an old idea gets dismissed by citation rather than on its merits.
What can go wrong
- The alternatives section says "we considered other options" and names none, which makes the record useless for its main purpose.
- The consequences section lists only benefits, so the record reads as a sales pitch and the next reader discounts the whole thing.
- The record is edited when the decision changes, destroying the history and leaving a document that claims a decision was made for reasons that were actually invented later.
- ADRs become a required gate for every change, so people write them for trivia, the collection becomes unsearchable, and the practice is abandoned along with the useful ones (Toil).
- They are written diligently for a year and then stop, leaving a collection that appears complete up to a date and silently is not.
- An ADR depends on nothing, which is its defining structural property and the reason it is the only document in this module that cannot become false.
- Later decisions depend on earlier ones, and the supersession chain is the design history — which is why removing a superseded record breaks the chain.
- The code depends on the decision, invisibly. A link from the relevant module to the record makes that dependency findable from the code rather than only from the index.
- "ADRs are for architecture only." The name is historical. Anything non-obvious and expensive to reverse qualifies — a dependency choice, a data format, a testing strategy, an internal convention with teeth.
- "Update the ADR when things change." Never. Write a new one that supersedes it. The old record's value is that it says what was believed then, and editing it deletes exactly that (Decision Records).
- "If it is in the code, we do not need it." The code records the decision and never the alternatives, and the alternatives are what prevent the re-proposal.
- "We need a process." You need a directory, a number and a template that fits on a page. Processes that require approval turn a fifteen-minute note into a project and the practice dies (RFCs).
Testing it, and how it ages
- The test for an ADR is whether someone who was not present can read it and correctly predict which arguments will be raised against the current design. If they cannot, the alternatives section is too thin.
- The other test is whether it still makes sense after the decision is reversed. A record that reads as embarrassing after reversal was written as advocacy rather than as reasoning.
- Nothing automated applies. This is a document about the past, and the only useful check is a human one at review time (Design Review).
- The collection is the asset. One ADR is a note; forty in order are the design history of the system and the fastest way for a new senior engineer to understand why anything is the way it is.
- Superseded records become more valuable over time, not less, because they contain the arguments that will be made again.
- Practices drift toward heaviness: templates grow sections, sections become mandatory, and the records stop being written. Cutting the template back is periodic maintenance of the practice itself.
Where this applies
This domain's advice is contested more than most. These labels say what each claim is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view rather than a caricature.
- GENERALThat a record of the past cannot become false, while a description of the present can, is a structural property of the two kinds of document and holds for any team, language or domain.
- LIFETIME-SPECIFICFor a system with a two-year horizon and a stable team, the reasoning stays in people's heads and the practice mostly does not pay. It becomes valuable precisely when the code will outlive the people — which is most production systems, and is always discovered later than it happened.
- SCALE-SPECIFICAt five people in one room, a decision plus its alternatives survives in shared memory for a couple of years. Across several teams, or with normal turnover, the memory is gone in months and the same proposal returns on a cycle — which is where the fifteen minutes starts to look cheap.
Where the depth lives
This domain teaches the codebase-level structure and hands the rest off.
- — System Design — most decisions worth recording are the ones where a system-level constraint (ordering, residency, latency budget) forced a structural choice, and the record is where that constraint stops being invisible.