Topic: how to update an adr
How to Update an ADR (Without Breaking the Audit Trail)
ADRs are immutable — but three things still need updating, and most teams get the protocol wrong on at least one of them. This is the operational guide: the status state machine, the supersession protocol with the bidirectional integrity check, and the right home for context shifts that don't change the decision.
TL;DR
The body of an ADR — Context, Decision, Consequences — is immutable. Three fields are not: (1) Status moves through a small state machine (Proposed → Accepted → Superseded / Deprecated / Rejected); (2) Supersedes / Superseded-by links update bidirectionally when a new ADR replaces an old one; (3) a Notes section at the bottom captures dated context shifts that don't change the underlying call. Editing Context, Decision, or Consequences after Accepted is the failure mode that kills the audit trail — every reader ends up looking at a record that's been rewritten to match newer thinking, with no way to see what the team actually believed at the time. The supersession protocol is two files, not one: write the new ADR, edit the old ADR's status + add a Superseded-by line, commit both atomically. A CI job catches one-sided supersessions on a nightly schedule.
Why immutability is the practice
ADRs exist to answer the question "why did we pick this?" eighteen months later, when the original decider has left and the new engineer is staring at production code that doesn't make sense. The only way that question stays answerable is if the rationale recorded at the time is still the rationale visible in the file today. The moment a team starts editing the body of accepted ADRs to match newer thinking, future readers can't tell which parts of the document represent the original reasoning and which are revisionist polish. The audit trail collapses — and the practice loses the only thing that made it worth doing.
Immutability is the practice, but it's not literal. Three things genuinely do need updating over time, and the discipline is to confine all updates to those three:
- Status — the lifecycle marker. Decisions are accepted, then sometimes superseded or deprecated. Updating status is not a body edit.
- Supersession links — the bidirectional pointer between an old ADR and the new one that replaces it. Updating these on the old ADR is the single legal body-edit, and only because the old ADR has no other way to point to its replacement.
- Notes section — a dated, append-only log at the bottom of the file for downstream context that doesn't reverse the decision but is worth recording near it.
Anything else — sharper wording, additional pros and cons, "what we didn't realize at the time" — should go in a new ADR (if the decision is now wrong) or a Notes entry (if the decision still holds). Never in the original body.
The status state machine
Most ADR templates ship with a single-word Status field but don't document the legal transitions. Here's the small machine that survives contact with reality:
| From | To | When | What edits the file |
|---|---|---|---|
| — | Proposed | ADR PR opened, decision under review | Initial commit creates the file with Status: Proposed |
| Proposed | Accepted | Reviewers approve, PR merges | Edit Status to "Accepted" + add Date in the same merge commit (or on merge if your template uses a commit-time hook) |
| Proposed | Rejected | The team decides not to proceed but wants to record that the option was considered | Edit Status to "Rejected" + add a brief Rejection-rationale paragraph in the Decision section. The file ships rather than getting deleted — rejected options are valuable record. |
| Accepted | Superseded by ADR-NNNN | A new ADR replaces this one | Edit Status to "Superseded by ADR-NNNN" + add Superseded-by: ADR-NNNN near the top. NEVER edit Context / Decision / Consequences. |
| Accepted | Deprecated | The decision still stood at the time but is no longer relevant — the system it referred to no longer exists | Edit Status to "Deprecated" + add a Deprecation-rationale Notes entry. No new ADR is required because there's nothing replacing the decision; the world it referenced went away. |
The two illegal transitions to watch for: Accepted → Proposed (don't reopen merged decisions for re-discussion in-place — write a new ADR if you want to revisit) and Superseded → Accepted (a superseded ADR stays superseded forever, even if the team later concludes the supersession was wrong; in that case the supersession itself gets superseded by a third ADR).
The supersession protocol — three steps, two files
Supersession is where most ADR practices fail their own audit trail. The mechanic isn't hard, but skipping any of these three steps leaves the directory in an inconsistent state that the next reader can't trust.
Step 1 — Write the new ADR
Treat it like any new ADR. Take the next number in sequence (the auto-numbering job at adr-github-action handles this without collision on rebase), write Context / Decision / Consequences at the time of the new decision, and add a single line near the top:
# 0042. Move from BullMQ to Redis Streams
Status: Accepted
Date: 2026-04-30
Supersedes: 0017-pick-bullmq-as-job-queue.md
## Context
...
The Supersedes line names the old ADR by number AND slug — both, because numbers can theoretically collide if the merge went sideways and the slug disambiguates.
Step 2 — Edit the old ADR (the only legal body-edit)
Open the old ADR. Make exactly two changes:
- Change
Status: AcceptedtoStatus: Superseded by ADR-0042(or whatever the new number is). - Add a new line near the top, just under Status:
Superseded-by: 0042-move-from-bullmq-to-redis-streams.md.
Do NOT touch Context / Decision / Consequences. The original rationale stays as-is. The new file gets the new rationale; the old file gets the pointer to find it.
Step 3 — Commit both files in one PR
Atomically. A PR that lands the new ADR without updating the old one is a one-sided supersession — the new ADR points back, the old ADR still claims to be Accepted, and the directory's index lies. The CI integrity check (next section) catches this on the next nightly run, but the audit trail has already been wrong for hours by then. Land both files together.
The bidirectional supersession integrity check
This is the operational test that prevents the most common failure mode. Run on a CI schedule (nightly is enough — the failure mode lands quietly so a once-a-day check catches it before any real reader hits the broken state):
# for every ADR with a Supersedes line, the target ADR must exist
# AND must contain a Superseded-by line back-pointing to the source.
for f in doc/decisions/*.md; do
superseded=$(grep -m1 '^Supersedes:' "$f" | awk '{print $2}')
[ -z "$superseded" ] && continue
target="doc/decisions/$superseded"
if [ ! -f "$target" ]; then
echo "BROKEN: $f names Supersedes: $superseded but file does not exist"
exit 1
fi
if ! grep -q "^Superseded-by:.*$(basename "$f")" "$target"; then
echo "ONE-SIDED: $f supersedes $superseded but target lacks a back-pointer"
exit 1
fi
done
This catches three real failure modes: (1) a Supersedes line pointing at a file that was renamed or deleted (broken pointer); (2) a new ADR landed with a Supersedes line but the old ADR's status was never updated (one-sided supersession); (3) the old ADR's status got updated manually but the back-pointer was forgotten (half-sided supersession). All three look fine in isolation; only the bidirectional check surfaces them.
The check is the third job in the four-job ADR GitHub Action workflow, which combines structure-lint, numbering-collision detection, supersession integrity, and auto-index rebuild. Path-filtered for PRs (zero CI minutes when no ADRs change) and nightly-scheduled to catch drift the path filter misses.
The Notes section — for context shifts that don't change the decision
Most ADR templates don't include a Notes section, and most ADR practices fail at the eighteen-month mark partly because of it. Real decisions accumulate downstream context: a third alternative appears, the cost of a known trade-off changes, you learn something that confirms the call. None of that warrants a new ADR if the underlying decision still holds — but it's worth recording near the original record so future readers see it.
The protocol: a single ## Notes section at the bottom of the file, dated entries appended below it. Never inline-edited into Context, Decision, or Consequences. Example:
## Notes
### 2026-08-12 — Postgres 18 vector indexes
Postgres 18 shipped pgvector with HNSW support, which closes the
similarity-search gap that originally pushed us to evaluate MongoDB
Atlas Vector. Decision still holds — we still picked Postgres for
the right reason — but the rationale "Postgres lacks vector search"
is no longer true.
### 2027-02-04 — RDS to Aurora migration
Moved this database from RDS Postgres to Aurora Postgres. The
ADR reasoning didn't change (Postgres-vs-MongoDB is still the live
trade-off); the engine choice within the Postgres family is captured
in ADR-0089.
Two or three Notes entries on a single ADR is fine. If you reach five, the underlying decision has probably drifted enough that supersession is the right move — write the new ADR, mark this one Superseded, and let the Notes section's history live in git rather than at the bottom of an outdated record.
What NOT to do
- Don't edit Context, Decision, or Consequences after Accepted. The single most common failure. If the wording was unclear, file it as a fix in a new ADR or a Notes entry. The original gets to stay wrong-feeling because that's what the team actually wrote.
- Don't delete superseded ADRs. A deleted ADR breaks every back-pointer that referenced it. Mark Status: Superseded; let the file live forever. Disk is cheap; broken links are expensive.
- Don't rename ADR files after they're accepted. The slug is part of the supersession pointer. Renaming breaks every Supersedes / Superseded-by line that named the old slug. If the title was wrong, the right move is to write a new ADR with the corrected framing and supersede the original.
- Don't update Status without dating the change. "Status: Superseded by ADR-0042" with no date is half-information. Add a Date field next to Status so readers can see when the supersession landed without grepping git log.
- Don't open a new ADR for trivial wording fixes. Typos, broken links, and code-block formatting are git-commit fixes, not new ADRs. The discipline is "no body edits to the decision content" — it's not "no commits ever to this file."
- Don't merge a one-sided supersession. The CI check catches it nightly, but a one-sided merge leaves the index lying for hours. Land both files in one PR.
How WhyChose fits in
This page is about updating ADRs you already wrote — keeping the audit trail honest as decisions evolve. The harder gap is the ADRs that never got written because the decision was made in a ChatGPT or Claude tab and the rationale walked off in someone's chat history. That's what the WhyChose extractor is built for: it reads your AI chat exports, surfaces every decision-shaped exchange with the trade-offs that were considered, and outputs structured records you can promote into ADRs in your repo. The threshold for promotion is the four-question test at when to write an ADR; the supersession protocol on this page is what keeps the directory honest after that. The extractor + the supersession discipline together solve the two failure modes that kill ADR practices: chats that never become ADRs, and ADRs that drift silently from the truth.
Related questions
Can I just edit an existing ADR if our thinking changed?
No — ADRs are immutable in body. Write a NEW ADR, mark it Accepted, mark the old one Superseded, and add bidirectional Supersedes / Superseded-by links. Status, supersession links, and a Notes section are the only fields that should ever change after Accepted.
What's the supersession protocol — exactly which fields update on which file?
Three steps. (1) New ADR with next number, Status: Accepted, and a Supersedes line naming the old ADR by number and slug. (2) Edit the OLD ADR — Status to Superseded by ADR-NNNN + add Superseded-by line near the top. (3) Commit both files atomically in one PR.
What if the context shifted but the decision is still correct? Do I need a new ADR?
No. Append a dated entry to a Notes section at the bottom of the existing ADR. Keep Context / Decision / Consequences frozen. Two or three entries is fine; if you reach five, supersession is the right move.
How do I catch one-sided supersessions automatically?
Nightly CI job: for every ADR with a Supersedes line, confirm the target file exists AND has a Superseded-by line back-pointing. Failures fail the job. Full workflow at adr-github-action — the supersession integrity check is one of four jobs.
Should I delete an ADR that turned out to be wrong?
No — mark Status: Superseded by the new ADR that captures the correction. Deleting breaks every back-pointer and every git-link in older PRs. The audit trail's job is to preserve "what we believed at the time" — wrong belief recorded honestly is more valuable than no record at all.
Further reading
- When to write an ADR (and when you shouldn't) — the threshold question this page assumes you've already passed.
- The ADR GitHub Action workflow — the four-job CI workflow that includes the supersession integrity check.
- The Nygard ADR template — the canonical body shape (Status, Context, Decision, Consequences) that this page's update protocol assumes.
- ADR template in Markdown — drop-in template you can use today.
- MADR (Markdown ADR) template — if you need YAML frontmatter for tooling, MADR's status/date fields map directly onto this update protocol.
- ADR vs Design Decision Record — disambiguation if you're not sure which kind you're updating.
- How to document architecture decisions — the practice-level guide that frames why this update protocol matters.
- The open-source extractor — surfaces decisions from your AI chat exports so they reach the repo before they need updating.