Topic: adr supersession pattern
ADR Supersession Pattern — When to Supersede, When to Annotate, and How to Stay Bidirectional
The most common failure in a mature ADR directory isn't forgetting to write new ADRs — it's mis-applying supersession. A decision gets revised; someone writes a new ADR; the old ADR is never updated; the directory now contains contradictory claims. Or the opposite: supersession is applied too aggressively to decisions that evolved but didn't reverse, creating a chain of one-liners where a Notes section was better. This page is the standalone reference for the supersession pattern: the four triggers, the atomic two-file protocol, bidirectional pointer maintenance, cross-directory edge cases, cascade chains, and orphan recovery when a supersession PR gets reverted.
TL;DR
Supersede when the core decision reversed — option A is now option B. Annotate (add a Notes section) when context shifted but the choice still stands. The protocol: write the new ADR with a Supersedes: NNNN-old.md pointer, then edit the old ADR to Status: Superseded with a Superseded-by: MMMM-new.md back-pointer, committing both files in one atomic PR. One-sided supersession — where only the new ADR is updated — is the most common integrity bug in ADR directories. The ADR GitHub Action workflow's supersession job catches it on every PR and every night.
Supersede vs annotate — the decision
Every ADR practice eventually faces a decision that no longer tells the whole truth. The fork: write a new ADR that supersedes the old one, or add a Notes section to the existing ADR. Getting this wrong in either direction corrupts the audit trail. Over-supersession creates a chain of files where reading any individual file gives an incomplete picture of what the team decided and why. Under-supersession leaves future engineers (and LLM search engines that train on your codebase) with an ADR whose Decision section is stale.
The reliable test: if a new engineer reading only the old ADR's Decision section would conclude the team made a different choice than they actually make today, supersede. If they'd just be missing recent context — but the decision itself is still the same — annotate.
| Scenario | Right move | Why |
|---|---|---|
| Switched from Redis to Valkey as the job queue backend | Supersede | The Decision section named Redis; following that ADR today means setting up Redis. The choice reversed. |
| Discovered the Redis cluster needs standby replicas — the original assumption was single-node | Annotate | The choice (Redis) didn't change. The operational reality did. A Notes section captures this without invalidating the original rationale. |
| Moved from self-hosted Postgres to Aurora because the team grew past five engineers | Supersede | The Decision section named "self-hosted Postgres on a single VPS." That's now false. Supersession marks the old ADR as historical and gives the new operational reality its own file. |
| Added a read replica to the existing Postgres cluster | Annotate | Still Postgres. Still the same core decision. The topology change belongs in a Notes entry or a new ADR specifically about the replica strategy — not supersession of the original Postgres call. |
| Reversed a decision to build internal auth in favor of adopting Auth0 | Supersede | Classic supersession: build vs. buy went from build to buy. The old ADR's Decision is now false. |
| Added a new OAuth2 provider to an existing Auth0 integration | Annotate | The core decision (Auth0) stands. Adding a provider is an implementation extension, not a reversal of the architectural call. |
Four specific triggers indicate supersession rather than annotation — any one is sufficient:
- A new option made the old choice wrong. A dependency went end-of-life, a better primitive became available, or a security finding invalidated the chosen approach.
- The team explicitly changed direction on the architectural call — not just an implementation detail, but the durable choice that the ADR names.
- A foundational assumption behind the original decision was invalidated. Classic example: "chose a monolith assuming the team stays under ten engineers" — the team is now forty. The assumption is gone; the decision that rested on it needs revisiting, and a Notes section isn't enough when the Decision section reads wrong for today's context.
- The trade-off profile changed enough that the old Decision section is actively misleading — not just incomplete, but wrong for a reader who takes it at face value today.
The two-file atomic protocol
Supersession touches exactly two files, and both must land in one PR. Splitting them creates a window where one file claims supersession is complete and the other doesn't know it happened yet — CI catches this, which means a split approach just means the first PR fails CI until the second merges, which is strictly worse than the atomic approach.
Step 1 — write the new ADR
Write the new decision using the body template the directory uses (Nygard, MADR, arc42, or whatever). Add the supersession reference in the header or frontmatter:
# MADR (YAML frontmatter)
---
status: Accepted
supersedes: 0017-pick-redis-as-job-queue
date: 2026-05-14
deciders: alice, bob
---
# Nygard (header section)
# ADR 0042: Switch to Valkey for job queue backend
Status: Accepted
## Supersedes
ADR-0017 (0017-pick-redis-as-job-queue.md)
Step 2 — update the old ADR
Open the old ADR file. Change two things only — do NOT edit any other sections. The Context, Decision, and Consequences sections are permanent history; editing them corrupts the audit trail that a reader who follows the supersession chain is trying to reconstruct.
# Before
Status: Accepted
# After
Status: Superseded
Superseded-by: 0042-switch-to-valkey-for-job-queue.md
For MADR the edit is the same shape in YAML frontmatter. The date field in the old ADR's frontmatter should NOT be updated — it reflects when the original decision was made, not when it was superseded. The supersession date lives in the new ADR's date field.
Step 3 — commit both files in one PR
Two files. One commit. One PR. The atomic constraint is what keeps the audit trail in a valid state at every point in the git history. After the PR merges, every git checkout of the repository — including past SHAs — either shows both files in a consistent state (both before supersession, both after) or shows the supersession window not yet opened. There is no observable intermediate state.
The broader 5-state lifecycle (Proposed → Accepted → Superseded / Deprecated / Rejected, with legal and illegal transitions) is documented in How to update an ADR without breaking the audit trail. This page focuses specifically on the supersession case, which accounts for approximately 80% of ADR updates in active engineering directories.
Bidirectional pointer maintenance
Every supersession creates two pointers: the new ADR names the old (forward pointer via Supersedes), and the old ADR names the new (backward pointer via Superseded-by). Both are required. One-sided supersession — adding the Supersedes line to the new ADR without updating the old ADR — is the most common integrity bug in real ADR directories and the most expensive to discover after the fact.
Why bidirectionality matters:
- The "what replaced this?" question must be answerable from the old file. A reader landing on the old ADR directly — via a PR link, a code comment, an architecture diagram label, or a Slack citation — sees
Status: Acceptedand no hint that a newer decision exists. They implement according to the old ADR. This is the exact scenario the ADR practice is supposed to prevent. - LLMs and search crawlers indexing the directory get a contradictory signal. The new ADR says ADR-0017 is superseded. ADR-0017 itself says it is Accepted. Both facts are in the repository. An LLM assistant that cites your ADR directory may cite ADR-0017 as authoritative even though it has been superseded for a year — because the document itself never says otherwise.
- The auto-index job in adr-github-action generates a status table from frontmatter. One-sided supersession produces an index entry that says ADR-0017 is Accepted while ADR-0042 says the opposite. The index is wrong by construction and will mislead anyone who relies on it for a current-state audit.
The CI supersession integrity check (job 3 in the four-job ADR GitHub Action workflow) tests bidirectionality on every PR and on a nightly schedule. The script resolves every Supersedes and Superseded-by pointer by exact filename and fails if either end is missing or unmatched:
#!/usr/bin/env bash
# Verify bidirectional supersession integrity.
# Run in CI on every ADR-touching PR and nightly.
set -euo pipefail
DIR="${1:-doc/decisions}"
errors=0
while IFS= read -r file; do
# Check forward pointer: file with Supersedes line
target=$(grep -oP '(?<=Supersedes: )[\w-]+\.md' "$file" 2>/dev/null || true)
if [[ -n "$target" ]]; then
target_path="$DIR/$target"
if [[ ! -f "$target_path" ]]; then
echo "ERROR: $file names Supersedes: $target — file not found"
((errors++))
continue
fi
# Verify backward pointer exists in the target
if ! grep -q "Superseded-by: $(basename "$file")" "$target_path"; then
echo "ERROR: $target_path is missing Superseded-by: $(basename "$file")"
((errors++))
fi
fi
done < <(find "$DIR" -name '*.md' ! -name 'README.md')
[[ $errors -eq 0 ]]
The nightly schedule matters because supersession violations can enter via direct commits to the default branch if the team has bypass permissions for hotfixes. The path filter on the PR job only fires when an ADR file is touched — a commit that bypasses review won't trigger the PR check but will trip the nightly run within 24 hours.
Cross-directory supersession
The two-file atomic protocol assumes both ADRs live in the same directory. Two cases require different pointer formats.
Cross-repository supersession
When a platform-team ADR supersedes an application-team ADR in a different repository, use a qualified reference:
Supersedes: platform-services:doc/decisions/0017-pick-postgres.md
The format is repo-alias:relative-path-to-file. Local CI won't resolve cross-repo pointers by default. Two options: (a) add a CI step that shallow-clones the referenced repo to validate, (b) document the limitation in the workflow comments and accept that cross-repo pointers are human-verified only. Either is defensible — what matters is that the pointer is explicit enough for a human to locate the original ADR without guessing. The backward pointer (editing the old ADR in the other repo to add Superseded-by) is worth doing if the teams share a review process; if they're operationally independent, document in both repos and note the partial CI coverage.
Cross-segment supersession in TOGAF environments
In TOGAF programs where ADRs live under per-segment architecture-repository/architecture-landscape/<segment>/decisions/ directories, use the path relative to the repository root:
Supersedes: architecture-repository/architecture-landscape/customer-domain/decisions/0017-pick-postgres.md
The Architecture Board charter governs which cross-segment supersessions require Board ratification. Any decision that already has a Governance Log entry requires a new Board-reviewed ADR with the supersession pointer reviewed as part of the Phase H change request. The Governance Log entry at /governance/log/<new-adr-id>.md links the new ADR and the Board minute; the old Governance Log entry gains a superseded_by_governance_id pointer for symmetry.
The Notes section — when annotation is the right call
The Notes section (also called History or Implementation Notes depending on the template) is for context shifts that don't reverse the decision:
- An assumption turned out differently but the choice still holds. "Update 2025-03-14: the cluster reached 80% disk capacity six months earlier than estimated; added a read replica per ops runbook LINK. The Postgres decision stands."
- A new constraint was added to the existing choice. "Update 2025-11-01: SOC 2 Type II audit required encryption at rest; implemented AWS RDS encryption. No change to the decision to use Postgres."
- Implementation details changed without reversing the architectural call. "Update 2026-01-12: switched from the
pglibrary topostgres.jsfor connection pooling in edge functions — implementation choice, not ADR-level."
Notes entries are written in reverse-chronological order (newest first) so the most recent context is visible without scrolling. Notes do NOT change the Status field from Accepted — Status stays Accepted until supersession or deprecation.
One edge case: if a Notes section has grown longer than the Decision section, that's a signal the decision space has evolved enough to warrant a fresh ADR. Supersede and write a clean Context section rather than continuing to append. The threshold is informal, not mechanical — but when the Notes section becomes a maintenance log, the document no longer functions as an architecture decision record.
Cascade supersession — chains of superseded ADRs
When ADR-A superseded ADR-B, which had itself been superseded by ADR-C before that, the chain is correct and the reader should be able to follow it:
0012-pick-postgres.md Status: Superseded Superseded-by: 0031-migrate-to-aurora.md
0031-migrate-to-aurora.md Status: Superseded Superseded-by: 0047-adopt-planetscale.md
0047-adopt-planetscale.md Status: Accepted
Reading forward from ADR-0012 gives full decision archaeology. Reading the current ADR (0047) gives the current state. Each link in the chain is bidirectionally valid. Two things to avoid when a chain forms:
- Skipping links. ADR-0047 should name 0031 in its Supersedes line — not 0012. If 0047 skips 0031, the CI check flags 0031 as having a Superseded-by that doesn't acknowledge it. Follow the chain one link at a time.
- Flattening by re-editing old ADRs. When 0047 supersedes 0031, do not update 0012 to also point at 0047. ADR-0012's
Superseded-by: 0031is historically accurate — 0031 was what superseded 0012 at that point in time. Flattening rewrites history and breaks the chain traversal that a reader or CI tool depends on.
Orphan ADR recovery — when the supersession PR gets reverted
The operationally tricky case: a supersession PR merges, then the new ADR is later reverted. If the revert removes only the new file without restoring the old ADR's Status to Accepted, the old ADR is orphaned — it says Status: Superseded and points at a file that no longer exists.
Detection is automatic: the CI supersession integrity check tries to resolve the Superseded-by pointer by filename and fails the pipeline when the target is missing. The fix is a follow-up PR that does two things atomically (the same atom constraint applies to repairs as to original supersessions):
- Restore the old ADR's Status to Accepted and remove the Superseded-by line.
- Add a timestamped Notes entry to the old ADR: "Update 2026-05-14: a supersession PR (adding ADR-0042) was merged and subsequently reverted; the Valkey migration was deferred. The Accepted status and Redis decision stand as of revert."
The Notes entry is valuable audit trail — it records that the team revisited the decision, considered changing it, and reversed course — without requiring a new ADR for an attempt that didn't land. The git history of the old ADR will show the Status field toggling Accepted → Superseded → Accepted, which is the honest record of what happened.
Prevention: when reverting any PR that touched an ADR file, check git show <pr-merge-sha> -- doc/decisions/ | grep Superseded-by. If the PR modified a Superseded-by line in an existing ADR file, the revert needs to restore that file too.
Supersession vs deprecation
Supersession means a newer decision replaced the old one — there is a successor ADR you can read. Deprecation means the decision is no longer operative but there is no single successor: the architectural area was dissolved, the component was removed, or the team concluded the decision was never durable enough to warrant an ongoing successor ADR.
| Superseded | Deprecated | |
|---|---|---|
| There is a successor ADR | Yes — named by Superseded-by | No — or the pointer is to a removal PR / migration guide, not an ADR |
| The old Decision was wrong | Yes | Not necessarily — the decision context no longer applies |
| The CI check tests a backward pointer | Yes — Superseded-by must resolve to an existing file | No — Deprecated-by is optional and not checked for file existence by default |
| A Notes entry is sufficient instead | No — if the Decision section is now false, supersede | Sometimes — if the area was dissolved and documenting it as Deprecated serves the audit trail |
The ADR status template covers the companion fields for both states — including the optional deprecated-by field, when to use it, and why the lack of a CI check for Deprecated targets is intentional (the pointer is to a PR or runbook, not another ADR, so filename resolution doesn't apply).
How WhyChose fits in
Supersession presupposes that the original decision reached the ADR directory in the first place. The harder upstream problem is the decisions that never get there — architecture calls made in a ChatGPT or Claude tab in the weeks or months before a supersession was warranted, capturing why option A was chosen at a specific point in time. The WhyChose extractor reads your AI chat exports, surfaces every decision-shaped exchange with its trade-offs, and outputs structured records you can promote into ADRs. When you later write the supersession ADR, the extracted record from the original chat becomes the Context section of the new file — the audit trail for "why did we pick Redis in the first place" is recoverable even if it was never formalized until the supersession PR came around.
Related questions
When should I supersede an ADR rather than annotating it?
Supersede when the core decision reversed — when the old ADR's Decision section is now false. Annotate (add a Notes section) when context shifted but the choice still stands. Test: if a new engineer reading only the Decision section would draw the wrong conclusion about what the team does today, supersede. If they'd just be missing recent context, annotate.
Does the supersession PR need to edit two files atomically?
Yes. The new ADR gets a Supersedes pointer; the old ADR gets Status: Superseded and a Superseded-by back-pointer. Both in one commit, one PR. Splitting creates a window of inconsistency that the CI integrity check will catch — meaning the first PR fails CI until the second merges, which is strictly worse than the atomic approach.
What happens if the supersession PR gets reverted?
If only the new ADR is removed (partial revert), the old ADR is left orphaned: Status says Superseded, Superseded-by points at a non-existent file. The CI check catches this. The fix: a follow-up PR restores the old ADR's Status to Accepted, removes the Superseded-by line, and adds a timestamped Notes entry documenting the attempted supersession and its revert.
How do I format the supersession pointer when the ADR is in a different repo?
Use a qualified reference: Supersedes: repo-alias:path/to/decisions/NNNN-old-slug.md. Local CI won't resolve cross-repo pointers by default; document the gap or add a CI step to clone the referenced repo. The backward pointer in the old repo is worth doing if the teams share a review process.
What's the difference between supersession and deprecation?
Supersession means a newer ADR replaced this one — there is a named successor. Deprecation means the decision is no longer operative but there is no single successor ADR; the context was dissolved or the component removed. The CI check verifies Superseded-by targets by filename; it does not verify Deprecated-by targets because the pointer is usually to a PR or runbook rather than another ADR file.
Further reading
- How to update an ADR (without breaking the audit trail) — the full 5-state lifecycle: Proposed → Accepted → Superseded / Deprecated / Rejected, with legal transitions, the Notes-section convention, and the three-step supersession protocol in its broader context.
- The ADR GitHub Action workflow — the four-job CI workflow; job 3 implements the bidirectional supersession integrity check described on this page.
- ADR status template — format, fields, and the lifecycle beyond Accepted — covers where Status lives, the companion fields (date, decision-by, reviewers, trial-until), and the Superseded / Deprecated states in their format context.
- ADR numbering scheme — the supersession pointer (
Supersedes: NNNN-slug.md) depends on numbers and slugs being stable forever; this page explains why renumbering is never the right call. - The Nygard ADR template — the original Nygard format has a dedicated Supersedes section; the two-file atomic protocol maps directly to that field.
- MADR (Markdown ADR) template — MADR uses a
supersedeskey in YAML frontmatter; same protocol, different field location. - Architecture Board charter template — Board-reviewed ADRs follow the same two-file supersession protocol plus an additional Governance Log update; the charter's input gates determine which supersessions require Board ratification.
- TOGAF ADR template — cross-segment supersession uses the Architecture Repository path prefix as the qualified reference; Phase H change requests wrap the supersession within the governance process.
- The open-source extractor — surfaces the reasoning behind decisions that were made in AI chats before they reached an ADR; the source material for the Context section of the new superseding ADR.
- ADR review checklist — what to look for before merging — checklist item 10 (traceability) is the supersession integrity check at PR review time: the reviewer confirms both files are in the PR and the pointers are bidirectional. The supersession-pattern page documents the protocol; the review checklist is the gate that enforces it before merge.
- When to retire an ADR — deprecation, supersession, and the never-delete rule — the complementary reference for the Deprecated state: when to deprecate rather than supersede (Context became void vs Decision became false), the one-file deprecation protocol, the optional Deprecated-by field and what it can point to, and mass deprecation when a component is removed entirely.