Decisions — the load-bearing artefact
Every consequential choice recorded once, with the options you rejected and the quality goal it serves. Not reconstructed from a diagram two years later by someone who was not in the room.
Spine concern · arc42 §9 · spine.yaml: decisions
An architecture decision is a choice that is expensive to reverse and that serves a named quality goal. Record it once, in the repo, with the options you rejected and the consequences you accepted. Accepted decisions are never edited — they are superseded by a new one that links back, so the history stays readable to whoever, or whatever, arrives later.
The Basics
Durable. What this concern is responsible for, regardless of decade or stack.
A system's structure can be read off the code. Its rationale cannot. Decisions are the only part of an architecture that is genuinely unrecoverable once the people who made them leave, which is why they sit at the front of the spine and everything else is derived from them.
What counts as architectural
Three tests, and a choice needs to pass at least one. Is it expensive to reverse — measured in weeks of work or a data migration, not in an afternoon? Does it serve a named quality goal, so that changing it changes what the system is good at? Does it constrain someone else — another team, a future service, an integration you have not built yet?
A choice that passes none of them is a preference. Do not record it. The fastest way to kill the practice is four hundred documents describing which date library the team likes, because then nobody can find the six that the design actually rests on.
The inverse mistake is rarer and worse: a decision made in a thread, agreed by four people, never written down, and load-bearing for everything built afterwards. That one costs a re-derivation every time a new person questions it, and eventually somebody wins the argument by attrition and reverses it without knowing what it was protecting.
The anatomy that survives contact with the future
Context: what was true when you decided, including the constraint you were working under. Options: the two or three you seriously considered, each with the reason you rejected it. Decision: one sentence, active voice. Consequences: what gets easier, and what you have now made harder or more expensive. Status and date. And the quality goal it serves.
The rejected options are the section that pays for the whole practice. Two years on, nobody is confused about what you chose — the code says that. They are confused about whether you considered the obvious alternative, and if the document does not answer that, the discussion restarts from zero.
Consequences must include the bad ones. A decision record listing only benefits is marketing, and it removes the one thing a future reader needs: whether the pain they are currently in was known and accepted, or unforeseen. Those two situations call for opposite responses.
Status is a lifecycle, not a label
Proposed, accepted, superseded, deprecated. An accepted decision is immutable — the moment you edit one to reflect what you now think, you have destroyed the record of what you thought before, and with it every clue about why the current shape of the system exists.
When a decision stops being right, write a new one. Give it a new id, state what it supersedes, and mark the old one superseded with a pointer forward. The old record stays exactly as written. A reader arriving at ADR-0007 is told, in one line, that ADR-0031 replaced it — and can then read both and understand the change.
This is the discipline that separates a decision log from a wiki page. Wiki pages record the present. Decision logs record the sequence, and the sequence is the argument.
Numbers are addresses
Allocate an id once and never reuse or renumber it. That id gets cited from a guardrail, a pull request, a code comment above the ugly workaround, a runbook, and an agent prompt. Renumbering breaks every one of those references silently.
Zero-pad to four digits and stop worrying about it. ADR-0007 sorts correctly in a directory listing, greps unambiguously, and does not collide with a ticket number.
Record the condition, not just the choice
Many decisions are correct only under conditions that will expire. A single Postgres instance is the right call while you are under a few thousand tenants; a shared queue is fine until two teams need different retention.
Write the condition down as part of the decision: "valid while X." That single line is what turns an annual review from an archaeology exercise into a filter — you are checking a handful of stated conditions against reality, not re-reading fifty documents asking yourself whether each one still feels right.
A decision record is complete when
- It names a goal
- The quality goal id it serves. If you cannot name one, you are choosing a technology, not making an architectural decision.
- It names the losers
- At least one seriously considered alternative, with the reason it lost. "We chose X" with no rivals is a note, not a decision.
- It admits a cost
- One consequence that is worse than before. Every real decision has one.
- It has a date and an owner
- Not for blame — for the review. Someone has to be asked whether it still holds.
- It is addressable
- A stable id, indexed in spine.yaml, so a check, a pull request or an agent can point at it.
In the spec
What the concern looks like once it is written down — the machine layer entries a build check or a coding agent resolves by id, and the prose beside them.
decisions:
- id: ADR-0009
title: Services own their data; no cross-service database reads
status: accepted
date: "2026-02-11"
serves: [QG-2]
enforced_by: [G-4]
file: decisions/ADR-0009-no-cross-service-db-reads.md
- id: ADR-0007
title: Single Postgres instance for all tenants
status: superseded
date: "2025-06-30"
superseded_by: ADR-0021
file: decisions/ADR-0007-single-postgres.md# ADR-0009 — Services own their data
Status: accepted
Date: 2026-02-11
Serves: QG-2 (change failure rate below 5%)
Enforced by: G-4
## Context
Two teams read the orders table directly. Every schema change
needs a three-team release window; the last one took nine days.
## Options
1. Shared database, coordinated migrations — rejected: the
coordination cost is the problem, and it grows per team.
2. Read replicas per consumer — rejected: same coupling, now
with replication lag in the failure modes.
3. Owning service exposes an API. Chosen.
## Decision
Only the owning service reads or writes its tables. Everyone
else goes through its API or its published events.
## Consequences
+ Schema changes ship without cross-team release windows.
- Two extra network hops on the checkout read path. Budgeted
in QG-1; revisit if p99 exceeds 400ms.
- Reporting loses ad-hoc joins. See ADR-0014 for the warehouse.
## Valid while
Fewer than ~15 services. Beyond that, revisit whether the
API-per-service hop count is still acceptable.The Current
Reviewed 2026-09-09How it actually plays out in production now. Dated, because this is the part that decays.
ADRs won the argument and then stalled in practice. Nearly every engineering organisation has heard of them, a large share have a decisions folder, and most of those folders stop at six entries written in the first fortnight of the practice. The format was never the problem. Nothing referenced them, so nothing kept them alive.
The state of the tooling
MADR has effectively become the default template, docs-as-code the default location, and Markdown-in-the-repo the default medium. Generators exist for the boilerplate — this site has one — and every serious documentation stack renders a decisions folder without configuration. None of that is a bottleneck any more.
What no tool supplies is the coupling. A decisions folder that nothing else in the repository mentions is a folder of essays, and essays lose to deadlines.
The one-command diagnostic
Search your repository for an ADR id outside the decisions folder itself. Guardrail configuration, code comments, pull request descriptions, the runbook, the spec — anywhere.
Zero hits means the practice is decorative, regardless of how many documents are in there. That is the most common result, and it is fixable in an afternoon by adding references rather than by writing more decisions.
Three couplings that keep them alive
Decision to quality goal. Every accepted decision names the goal it serves; every goal can list the decisions taken in its name. This is what makes a decision reviewable — if the goal changes rank, you know exactly which choices are now suspect.
Guardrail to decision. When a decision is worth enforcing, the check that enforces it cites the id. Now the decision has teeth, and a failing build points a developer at the rationale instead of at a rule they will read as arbitrary.
Pull request to decision. One line in the template: which decision does this change rely on, and does anything here contradict one? Most of the time the answer is neither, and it costs five seconds. The occasions when the answer is "it contradicts ADR-0012" are exactly the changes that should not merge on a Friday afternoon.
Where they live when you have forty repositories
Local decisions belong in the service that they constrain. Organisation-wide decisions — the identity provider, the event envelope, the deployment target, the language shortlist — belong in one platform repository, published by id, and referenced from the local spine rather than copied into it.
Copying is the failure. Two copies of a decision diverge, and the divergence surfaces during an incident when two teams cite the same id and mean different things.
Regulatory pull is now a real, secondary reason
Assurance regimes across finance, health and AI increasingly ask for documented rationale behind design choices, not just the resulting design. Teams that already keep decision records answer those requests by exporting a folder.
Treat that as a side benefit rather than the motivation. A decision log written to satisfy an auditor is written once, is immediately stale, and is the exact artefact this whole approach exists to avoid.
Future-ready
What changes when agents write and operate the code. Opinionated on purpose.
A coding agent can read the entire codebase and still not know why any of it is that way. The code carries what; only the decisions carry why, and what was already rejected. That makes the decisions folder the highest-value context you can hand an agent — and the part of the spec that fails most expensively when it is missing.
Without it, every session re-litigates a settled question
Ask an agent to add a feature to a system whose architecture it can only infer, and it will infer generously. It will propose the option you rejected, because that option is the most common one in its training data and nothing in your repository says otherwise. It will be articulate about it. It will be wrong in a way that takes a senior reviewer twenty minutes to explain — and the same twenty minutes again next week.
With the decision present and cited, this collapses to nothing. The agent reads that direct database access from other services was rejected in ADR-0009, and it writes the client against the API instead. The saving is not the one prompt; it is the recurrence.
Silent contradiction is the new failure mode
The old failure was documentation drifting behind the code slowly enough that people noticed. The new one is faster: a change that contradicts an accepted decision, generated in seconds, with a plausible commit message and clean tests, merged by a reviewer who never read ADR-0009 either.
Two defences, and you need both. The agent bundle makes reading arch/ and citing the decision part of the workflow before code is written. A guardrail catches the case where it did not. Prose alone is guidance; only the check is enforcement, and this is precisely why decisions and guardrails are adjacent concerns rather than one document.
Let agents draft decisions, not make them
Agents are genuinely good at the laborious parts of an ADR: enumerating the alternatives, articulating the trade-offs, listing consequences you would have skipped at 6pm. They are not accountable for the outcome, so they do not get to accept one.
The workflow that holds: an agent opens a decision in proposed status when it hits a choice the spec does not cover, and stops. A human accepts, amends or rejects. The status field, which used to be near-ceremonial, becomes the actual control boundary between what the agent may settle and what it may not.
Index in the machine layer, prose in the files
spine.yaml carries the id, title, status, date, the goal served, and the path to the file. Nothing else. An agent scans the index cheaply, then loads only the two decision files relevant to the change it is making.
Context budget is a real design constraint now. A machine layer that inlines the prose forces every agent to read every decision to find one, which in practice means the whole index gets dropped from the prompt and none of it gets read.
Immutability matters more, not less
When a human reads an edited decision, they lose some history. When an agent reads one, it loses the only evidence that the approach it is about to suggest was tried and failed. Superseding preserves the negative result, and negative results are the scarcest thing in any repository.
How this concern fails
Named, because a failure mode you can name is one you can spot in your own repository before it costs you a quarter.
The four-hundred-document archive
Everything got recorded, so nothing can be found. Recognisable by decisions about formatting, libraries and folder names. The fix is not curation after the fact — it is the three tests: reversal cost, quality goal, someone else constrained.
The ADR written after the merge
Ceremony backfilled to satisfy a checklist. It records what was built, never the rejected alternatives, and is therefore useless at the moment someone asks why. If the decision is already made in code, write a short one honestly framed as ratification, or write nothing.
The edited accepted decision
Someone updated ADR-0007 in place because it was 'out of date'. The history of why the earlier approach failed is now gone, and the next person — or the next agent — will propose it again.
The decision with no goal
It names a technology and a preference and no quality attribute. It cannot be reviewed, because there is no criterion against which it could turn out to be wrong.
The decisions behind a login
A wiki space with SSO. Humans will not open it mid-task and your coding agent cannot open it at all. If it is not in the repository, it is not in the loop.
Go deeper
- toolADR Generator
- postADRs people actually maintain
- glossaryADR
Frequently asked
How many architecture decisions should a service have?
For most services, six to fifteen accepted decisions cover the design. If you are past forty, either the system is genuinely large or preferences are being recorded as decisions — check whether each one is expensive to reverse, serves a named quality goal, or constrains another team. If a decision passes none of those tests, it is a note, not an architecture decision.
What is the difference between an ADR and a decision in spine.yaml?
They are the same decision at two levels of detail. The Markdown file carries the context, the rejected options and the consequences — the part a human reads. The spine.yaml entry carries the id, title, status, date, goal served and file path — the part a build check, a pull request template or a coding agent resolves by id. Keeping the prose out of the machine layer is deliberate: agents scan the index and load only the files that matter to the change in front of them.
Should I backfill decisions for an existing system?
Backfill the load-bearing six, not the history. Ask which six choices, if reversed, would require rewriting a significant part of the system — the data ownership model, the consistency stance, the deployment target, the tenancy model, the integration style, the authentication approach. Write those from memory, mark them as ratified rather than pretending they were decided this way, and stop. Full historical reconstruction is the reason most backfilling attempts are abandoned.
Can I edit an accepted ADR to fix a typo?
Typos and broken links, yes. Anything that changes the meaning — the decision, the options, the consequences — no. Write a new decision that supersedes it. The rule exists because the value of the record is the sequence: what was believed, what changed, and what that cost. Editing in place erases exactly the part that a future reader, or a coding agent proposing a previously failed approach, needs.
Should a coding agent be allowed to write ADRs?
Let it draft them in proposed status and never accept them. Agents are good at enumerating alternatives and consequences, which is the tedious half of the work. Accepting a decision means being accountable for the outcome, which is why the status field becomes the control boundary: an agent that hits an uncovered choice opens a proposal and stops rather than deciding by writing code.
How do ADRs relate to arc42 section 9?
arc42 places architecture decisions in section 9, near the back, as one of twelve sections describing the system. The Spine promotes them to first position and derives the rest — the building block view, the runtime view, the deployment view — from them. Same content, inverted centre of gravity: in arc42 the views are the deliverable and the decisions are supporting material; here the decisions are the artefact and the views are generated evidence.
The other eleven concerns
Pages are being written one at a time. The ones without a page yet are still in the template, with prompts instead of prose.
- Quality goals
- Guardrails
- Constraints
- Context & scope
- Solution strategy
- Structure
- Behaviour
- Deployment & operations
- Cross-cutting concepts
- Risks & debt
- Vocabulary
Decisions
Take the template, not the idea
The zip, the single-file variant, the schema and the agent bundle. Nothing behind an email address.
Free to fork, modify and use commercially. No attribution required.