· 6 min read ·

ADRs Work Because You Cannot Edit Them

Source: martinfowler

Architecture Decision Records have been around long enough that most developers have at least heard of them. Fewer teams actually keep them, and of those that do, many end up with a folder of stale markdown files that nobody reads. That outcome is usually a symptom of misunderstanding the practice, not evidence that the practice is flawed.

Martin Fowler’s bliki entry on ADRs is a good primer on the concept, but it leaves room to go deeper on the mechanics: what makes a good record, why immutability is the constraint that holds the whole system together, and what tooling makes the practice sustainable.

Where ADRs Come From

Michael Nygard popularized the format in a 2011 blog post where he observed that software documentation typically captures the current state of the system but loses the reasoning behind it. You can read the code to learn what the system does. You cannot read the code to learn why a particular approach was chosen over alternatives, what constraints existed at the time, or what trade-offs were accepted.

Nygard’s proposed format is intentionally minimal. Each record has five sections:

# ADR-001: Use PostgreSQL for the primary data store

## Status
Accepted

## Context
The application needs a relational store with strong consistency guarantees.
The team has existing expertise in PostgreSQL. We evaluated SQLite for its
operational simplicity but ruled it out due to write concurrency requirements.

## Decision
We will use PostgreSQL 15 as the primary data store. Migrations will be
managed with Flyway.

## Consequences
Operational complexity increases relative to SQLite. We gain full ACID
semantics, row-level locking, and a mature ecosystem of tooling. The team
will need to manage a PostgreSQL instance in every environment including
development.

The status field carries more weight than it appears to. It can be Proposed, Accepted, Deprecated, or Superseded. That last value is what makes the immutability constraint work in practice.

The Immutability Constraint

The standard advice is that you should never modify an ADR after it has been accepted. If the decision changes, you write a new record and mark the original as Superseded by ADR-007 (or whatever the successor is). This feels awkward at first, especially for developers conditioned to keep documentation up to date.

The constraint exists for a good reason. If you allow records to be edited in place, you lose the historical context. Someone reading the document six months later cannot tell whether the current text reflects the original reasoning or a series of revisions. The document loses its value as a record and becomes just another piece of documentation that may or may not reflect reality.

Immutability also creates an honest accounting of how architectural thinking evolves. A codebase with fifteen ADRs where three have been superseded tells a story. You can trace the sequence of decisions, understand what prompted each change, and see which early assumptions held up. That is information you cannot reconstruct after the fact.

Nygard’s original framing is worth quoting directly: “the motivation behind previous decisions is hard to reconstruct. But those old decisions aren’t worth remaking if you had to remake them for each project.” The goal is to reduce the cognitive load of rediscovering constraints and trade-offs that someone already worked through.

MADR and Other Format Variations

Nygard’s five-section format is not the only option. MADR (Markdown Architectural Decision Records) is a more structured alternative developed by Oliver Kopp that adds sections for Options Considered and Pros and Cons of the Options. It looks like this:

# Use a message queue for async job processing

## Status
Accepted

## Context and Problem Statement
Long-running jobs (report generation, email sends) were blocking request
handlers. We need a way to defer this work without coupling services.

## Considered Options
- BullMQ backed by Redis
- AWS SQS
- Database-backed polling queue (pg-boss)

## Decision Outcome
Chosen option: BullMQ, because the team already runs Redis and BullMQ's
priority queues fit our workload shape better than SQS FIFO queues.

### Consequences
- Good: Low latency job pickup, fine-grained priority control.
- Bad: Adds Redis as a required infrastructure dependency.

MADR is more verbose, but the explicit options section is valuable because it records what you decided against and why. The Nygard format often elides this, which means a future reader can see what was chosen but not what alternatives were evaluated.

A third approach worth knowing is the Y-statement format, which compresses the decision into a single structured sentence: “In the context of [situation], facing [concern], we decided [option], to achieve [quality], accepting [downside].” This is useful for quick decisions that do not warrant a full document, though it sacrifices the nuance that longer formats preserve.

Tooling That Makes the Practice Stick

Writing ADRs by hand in a text editor is fine, but tooling helps maintain consistency and discoverability. adr-tools by Nat Pryce is a shell-based CLI that handles the bookkeeping of creating sequentially numbered files, managing status transitions, and linking superseded records to their successors.

# Initialize ADR directory
adr init doc/adr

# Create a new record
adr new "Use Redis for session storage"
# Creates: doc/adr/0001-use-redis-for-session-storage.md

# Mark a decision as superseded
adr supersede 1 "Switch to in-memory session storage for single-instance deploy"

For teams that want a browsable HTML output from their ADR folder, Log4brains generates a static site from a directory of markdown ADRs. It supports both Nygard and MADR formats, renders the full decision history, and produces a graph of supersession relationships. This is useful when onboarding new team members who need to understand why the system is structured as it is without trawling through raw markdown.

AWS’s CDK project maintains a public ADR directory in their GitHub repository, which is a good reference for what the practice looks like at scale in an open-source project.

Keeping Them in the Repository

Storing ADRs in the source repository, typically under doc/adr or docs/decisions, ties the decision history directly to the codebase it describes. This has a few concrete benefits. First, ADRs can be reviewed in pull requests alongside the code changes they explain, which creates a natural forcing function for writing them at the moment of decision rather than retroactively. Second, the git history of the ADR directory provides a timeline with author attribution and timestamps, without any additional tooling. Third, engineers working in the codebase will discover the records through ordinary file browsing rather than needing to navigate a separate documentation system.

The lightweight markup requirement, almost always markdown, follows from the same logic. Records should be diffable, searchable with grep, and readable in any text editor. A decision captured in Confluence or Notion is better than nothing, but it is invisible to anyone whose first instinct is to look at the repository.

When ADRs Become Shelfware

The practice fails in predictable ways. Teams write ADRs for major architectural decisions but skip smaller ones, and the folder ends up representing only a fraction of the actual decision history. Alternatively, teams adopt ADRs as a mandatory process step and fill them with thin, post-hoc justifications that do not reflect real deliberation.

Fowler’s point about the act of writing being valuable in itself addresses the second failure mode. A record written to capture genuine reasoning, including the alternatives that were rejected and the constraints that shaped the choice, has value even if nobody reads it for two years. The discipline of putting the reasoning into words forces a level of precision that a verbal decision in a meeting does not.

The most sustainable approach is to start with a low bar. A one-paragraph ADR that captures a decision and its immediate trade-offs is more useful than no record at all, and it is far easier to maintain than a template with ten required sections. Reach for the more structured MADR format when the decision is consequential enough to warrant enumerating alternatives; use Nygard’s minimal format for everything else.

The goal is to answer, months or years later, the question that every engineer eventually asks when reading unfamiliar code: why on earth is it done this way. ADRs do not answer every such question, but they cover the decisions that someone made deliberately, and those are usually the ones worth understanding.

Was this interesting?