River
Y CombinatorBacked by Y Combinator
FREE TEMPLATE

Architecture Decision Record Template

Five documents and two sheets, including a register that reconciles every record's status against the words its file actually carries.

Free download  ·  No account needed

Record Template

NNNN. A title in the imperative, naming the decision

Date: YYYY-MM-DD  ·  Sequential, monotonic, never reused

Status

One state word from your own vocabulary, on its own line. Nothing else on that line: no parenthetical, no date, no qualifier. This is the field the register reconciles, and the field nothing downstream reads.

Context

The forces in play, in the present tense, without arguing for the outcome. The numbers somebody measured. What was not known.

Considered Options

Every option genuinely proposed, including the one chosen and including doing nothing. Not in the original format; added because a record naming only the winner cannot stop the argument recurring.

Decision

One paragraph, starting We will. Then why the winner won, then why each other option lost. Where nobody gave a reason, it says nobody gave one.

Consequences

Both directions. What gets easier, and what gets harder.

Review

Review due: date plus your cadence. What would change our mind: the observation that would make this decision wrong.

Every decision record has a Status heading, so knowing which decisions still hold looks like a lookup. It is not. Michael Nygard's original format hedges every part of that lifecycle with the word may, deliberately, and the tools each hardened a different part of it. What reached the field is a region of free text that can hold a state word, a link, both, or nothing at all, with nothing structural to tell them apart.

Two of the reasons are in the reference tool itself. It hardcodes the status when it creates a record, so every record is born accepted and no flag asks for proposed. Retiring one adds a link and then deletes the word, which its own test fixture asserts: the retired record's status section holds one line, and that line is a link. Where no records exist at all, the job is reading the decisions back out of the codebase instead.

So the register carries two status columns. One transcribes what the file literally holds, misspelling and all, and the other normalises it against the vocabulary a team settles once. The tool's own published folder is the argument for that: nine records over two and a half years, every one accepted, not one ever retired, and a single amendment where both sides correctly stayed in force. Send River whatever you hold, or take the Word documents and CSV sheets blank.

The decisions still in force, and the ones the records cannot answer

The Decision Register, the Link Map, and the three lists they produce.

Decision Register

Illustrative rows for a fictional team, Larkfield.

RecTitleStatus region, verbatimStatusReconciliation
0003Postgres for the primary storeAcceptedacceptedagrees
0005Sync writes to the search indexAccepted  /  Amended by [0011]acceptedagrees
0007Queue per tenantSuperceded by [0012]unknownno state word
0008gRPC between servicesAccepted  /  Superceded by [0013]ambiguoustwo states
0009Feature flags in Redisunknownno state word
0010Terraform for all environmentsCurrentunknownoutside the vocabulary

Two columns, not one. A register reporting only the normalised value has thrown away every distinction in the last column, and the distinction is the finding. Both spellings live in the sheet, in their own cells, so a search for either one lands on the row.

0008 says both. Not carelessness. The tool deletes the state word by exact string match, so a region hand-edited to lower case, or given a trailing period, keeps its word and gains the link.

Four of these six records cannot tell you whether the decision still holds.

Link Map

One row per edge, not per record, because a relation is a string somebody typed.

FromToRelation, as writtenReciprocalIn graph
00050011Amended byAmendsNo
00110005AmendsAmended byYes
00070012Superceded bySupercedesNo
00080013Superceded bymissingNo

The last row exists on one side only. 0008 says it was retired and 0013 never claims to have retired it, so which of the two is wrong is the interesting question, and nothing repairs it on the record's behalf.

Every reverse edge is absent from the diagram by design. The graph generator drops any relation whose string ends in the word by before drawing, so a pair that survives only on the reverse side appears nowhere and raises nothing.

Relation as written and relation normalised are separate columns, for the same reason status is.

What is still in force

Three lists. The third one is the point.

In forceNot in forceRecords cannot say
0003 Postgresnone0007, 0008, 0009, 0010
0005 Sync writes

0005 is in force, and it has been amended. An amendment extends a decision rather than retiring it, so the original keeps its status and gains a link. Filing one as a supersession retires a decision that still governs, and no tooling catches it.

Nothing has ever been retired here. Two records point at replacements and neither replacement claims the retirement, so the honest answer is that the folder records no completed retirement at all.

0007 almost certainly is retired. It says so with a link and it lost the word to the tooling. Until the conventions document says a link on its own is sufficient evidence, it stays unknown rather than becoming a guess with a green cell.

A published contents page generated from this folder would show all six, identically.

What's in the pack

01

Decision Register

One row per record, carrying the status region transcribed verbatim next to the normalised state, and how the two relate.

02

Link Map

One row per edge, holding the relation exactly as typed, whether the other side declares it, and whether the target resolves.

03

Recording Conventions

The ten questions a team has to answer once: the vocabulary, the path, the spelling, and the choices the well-known formats answer differently.

04

Reading a Status Region

Why the field is unreliable, with each mechanism read off the tool that causes it, and what to write in the register instead.

05

Record Template

The five original sections plus the options list and the line naming what would change the team's mind, with the guidance annotated inline. A record starts once the argument is settled, so the proposal that argued for it belongs in an RFC register instead.

06

ADR 0001 Record Architecture Decisions

The self-referential first record, written out in full, so the decision to keep records is itself argued rather than assumed.

07

ADR 0002 Read Replicas for the Reporting Queries

A finished record at the bar the rest should hit, including an option that lost with nobody arguing against it, recorded as such.

How to use it

  1. 1

    Open in River, or take it blank

    Open the pack in River and hand it the records, or download the Word documents and CSV sheets and fill them in yourself.

  2. 2

    Settle the conventions first

    The vocabulary, whether records start proposed, and how supersession is recorded. Until those are answered the register has nothing to reconcile against.

  3. 3

    Send the folder

    A doc/adr directory, a Confluence export, a pile of design docs, or the pull request thread where something got settled. Each status region is transcribed before it is interpreted.

  4. 4

    Read the three lists

    In force, not in force, and what the records cannot answer either way. The sweep re-reads every region weekly and reports what changed.

Frequently asked questions

Is this template free?

Yes. The zip is Word documents and CSV sheets, and it needs no account and no card. Edit with AI is the optional half: the agent reads the records you already have, transcribes each status region, and fills the register. The other packs sit in the template library.

What format are the downloaded files?

Word (.docx) for the five documents, CSV (.csv) for the two sheets, zipped together. Excel, Numbers and Google Sheets read the sheets straight off the download, and the records open in Word or Pages. There is nothing to convert and no proprietary format anywhere in it.

Can it tell me which of our records are still in force?

For the ones whose status is readable, yes, and it separates them from the ones nothing can answer. That second group is usually larger than teams expect, because the generator that prints the contents page makes no status call at all, so a retired decision has been sitting in the published index looking exactly like a live one.

Every record we have says Accepted. Is something wrong?

Probably not, and it also means less than it looks. The reference tool writes that word on creation and offers no way to create a proposed record, so uniformity is evidence about the tool rather than about agreement. The register reports it as a finding about the folder instead of quietly treating it as consensus.

A decision got made in a thread and never written up. Can it write the record?

Yes, and it reads the thread first: what was decided, by whom, which options were genuinely proposed, and why each one lost, quoted. Where nobody gave a reason, it records that nobody did. What it will not do is backfill a decision from three years ago whose argument is gone, because a record like that is a reconstruction that reads as evidence.

How is this different from a design document?

A design document describes an intended state and goes stale, with no moment at which anybody has to notice. A decision is an event, so a record of one becomes history rather than wrong. The solution architecture document and the system design document both reference the records. After an outage, the incident postmortem records what happened; any decision it forces gets its own record.

What does 'Edit with AI' actually do?

It signs you up, installs this pack as a private workspace of your own, and puts the agent in front of the register with nothing in it yet. Then you send what you hold, in whatever shape it is in: a repository folder, a wiki export, four files from 2019, or a thread.

Find out which of your decisions still hold

Take the Word documents and CSV sheets blank, or open this exact pack in River and let it read the records you already have.

Edit with AI