An ADR (Architecture Decision Record) is a short document, one or two pages long, that captures an important technical decision, its context and its consequences. The practice rests on three rules: one decision per file, a file that is immutable once accepted, and storage next to the code, in docs/adr/NNNN-title.md. The most widespread format is MADR (Markdown Any Decision Records). You write an ADR for any choice that is costly to undo: database, framework, message broker, language, protocol, split into services. This page explains what an ADR is for, when to write one, how to fill in the MADR template, how to bring the practice into a team and which anti-patterns kill it. It is written for developers, architects and tech leads who want to stop losing the memory of their choices.
What you will learn
- Define what an ADR is and tell it apart from classic documentation.
- Decide when to write an ADR (and when it is not worth it).
- Fill in the MADR template to structure a decision.
- Integrate the ADR practice into the team workflow without friction.
- Avoid the classic anti-patterns (ADR written too late, too long, or forgotten).
The scenario that justifies the practice
The following scenario is replayed in many organisations, with different technologies. A European bank, in 2019: a senior developer discovers a microservices architecture of 47 services talking through RabbitMQ. Nobody on the current team knows why this choice was made three years earlier. The original architect has left. The technical specifications are silent on the matter.
The team spends 6 months investigating before understanding that RabbitMQ had been chosen for its dead-letter queues, which were essential for regulatory compliance. Without that knowledge, they were about to migrate to Kafka, a decision that would have broken the requirements of banking regulation.
A simple two-page ADR would have saved those 6 lost months.
What is an ADR?
An Architecture Decision Record (ADR) is a short document that captures a significant architecture decision, its context and its consequences. The concept was formalised by Michael Nygard in November 2011 in his founding article "Documenting Architecture Decisions".
Nygard, who also wrote the book "Release It!", starts from a field observation: "nobody reads big architecture documents, but not knowing the reasons behind important architecture decisions can lead to disastrous consequences". The whole format follows from it. An ADR is short so that it gets read, it is dated to place the context of the time, and it keeps the rejected options because that information, not the chosen solution, is what the reader is missing three years later.
ADR vs classic documentation
| Aspect | Classic documentation | ADR |
|---|---|---|
| Length | Dozens of pages | 1-2 pages maximum |
| Scope | The whole system | A single decision |
| Updates | Rare (often outdated) | Immutable (you create a new ADR) |
| Question | "How does it work?" | "Why this choice?" |
| Audience | Newcomers | Future decision makers |
| Lifespan | Short (becomes outdated) | Permanent (history) |
Classic documentation explains the current state. ADRs explain the path travelled.
Why are ADRs essential?
A Git repository keeps what was done, never why. The reasoning, the rejected options and the constraints of the moment live in the memory of the people who were there, and disappear with them.
The cost of lost knowledge
Architecture decisions have a particular trait: they are easy to forget but expensive to rediscover.
Typical scenario:
- An architect makes an important technical choice
- They talk about it in a meeting and send an email
- 18 months later, they leave the company
- A new developer wonders "why PostgreSQL and not MongoDB?"
- Nobody remembers, the email cannot be found
- The team spends weeks analysing and benchmarking
- They make a decision... maybe the same one, maybe a different one
- The cycle starts again
Scenario with an ADR:
- An architect makes an important technical choice
- They write an ADR in 30 minutes
- 18 months later, they leave the company
- A new developer wonders "why PostgreSQL and not MongoDB?"
- They open the ADR
docs/adr/0015-use-postgresql-for-main-database.md - In 5 minutes, they understand the context, the alternatives evaluated and the decision
- They can either confirm that the context is still relevant, or create a new ADR if it has changed
The two scenarios read side by side on the diagram: same choice, same question eighteen months later, but a several-week investigation on one side and a five-minute read on the other. The dotted loop in the scenario without an ADR is the point to remember: the redone decision is no better written down than the first one.
The four benefits of a decision log
The main gain of an ADR is not documentary, it is about time: it removes the investigation work of rediscovering a past choice. These four effects appear once a repository has accumulated a few dozen ADRs.
| Benefit | What changes in practice |
|---|---|
| Faster onboarding | The newcomer reads ten dated decisions instead of questioning five people |
| Faster decisions | Options already evaluated are not re-analysed from scratch |
| Fewer "why?" questions | Interruptions of senior developers become rarer |
| Easier audits | The traceability of choices is available without reconstruction |
These benefits are hard to quantify, and the percentages that circulate on the subject are rarely sourced. One thing is easy to measure in your own team, though: the response time to the question "why is this component here?". Time it before adopting ADRs, then a year later, on three or four components picked at random.
Anatomy of an ADR
An effective ADR follows a standard structure. The most popular template is MADR (Markdown Any Decision Records), but the original template by Michael Nygard remains a reference.
The diagram walks through a complete ADR on a real case, the choice of PostgreSQL, in the order in which it is read: the context and the decision criteria, the options weighed, the decision taken, then its consequences.
Nygard template (original)
The original template is minimalist, and that is its strength:
# 1. Record architecture decisions
Date: 2024-01-15
## Status
Accepted
## Context
We need to record the architectural decisions made on this project.
## Decision
We will use Architecture Decision Records, as described by Michael Nygard.
## Consequences
See Michael Nygard's article, linked above. For a lightweight ADR toolset,see Nat Pryce's adr-tools.This format has four sections (Status, Context, Decision, Consequences), preceded by a numbered title and a date. These six elements are enough to make a decision understandable years later.
- Title: numbered, with a clear action ("Use X for Y")
- Date: when the decision was made
- Status:
Proposed,Accepted,DeprecatedorSuperseded - Context: why this decision is needed
- Decision: what was decided
- Consequences: positive and negative impacts
MADR template (enriched)
MADR (Markdown Any Decision Records) takes Nygard's skeleton and adds three sections that become necessary as soon as a decision is contested: Decision Drivers, Considered Options and Links. The decision drivers are the selection criteria, written before comparing the options: they are what stops the comparison from being written after the fact to justify a preference. The Considered Options section keeps the branches not taken, with their advantages, so they do not need to be re-evaluated later. The Links section ties the ADR to the others, through Supersedes and Superseded by, and forms the thread of the history.
The example below is complete: it reads from top to bottom the way the reasoning unfolded, from the problem to the accepted negative consequences.
# ADR-0042: Use PostgreSQL as the main database
## Status
Accepted
## Context and Problem Statement
Our application needs a relational database able to:- Handle complex ACID transactions- Store semi-structured JSON data- Support 10,000 read requests/second
The team has MySQL and MongoDB skills, but not PostgreSQL.
## Decision Drivers
* Performance under heavy read load* Native JSON support without sacrificing SQL joins* Licence cost (open source preferred)* Maturity of the ecosystem (tooling, monitoring)* Learning curve for the team
## Considered Options
### Option 1: MySQL 8.x
**Pros**:- Team already trained- Large community
**Cons**:- JSON support less mature than PostgreSQL- Lower JSON performance in our benchmarks
### Option 2: PostgreSQL 16
**Pros**:- Excellent JSON support (indexable JSONB)- Better performance in our tests (+30%)- Rich extensions (PostGIS, TimescaleDB)
**Cons**:- Team training required (2-3 weeks)- Some internal tools to adapt
### Option 3: MongoDB 7.x
**Pros**:- Schema flexibility- Team partially trained
**Cons**:- No reliable multi-document transactions- Higher cost at scale
## Decision
**We choose PostgreSQL 16** for all new applications.
Main reasons:1. Benchmarks show +30% performance on our use cases2. JSONB support lets us mix relational and document models3. The extension ecosystem meets our future needs (geospatial, time series)
## Consequences
### Positive
* Guaranteed performance for our use cases* Durable technology base (PostgreSQL is growing)* Ability to use advanced extensions
### Negative
* Training investment: 3 weeks for the team* Migration of internal tools: 2 sprints estimated* Need to hire a DBA with PostgreSQL expertise
### Neutral
* Existing MySQL applications stay on MySQL* Review in 12 months to validate the performance assumptions
## Links
* Supersedes: ADR-0023 (Use MySQL for all databases)* Benchmarks: /docs/benchmarks/database-comparison-2024.md* Related RFC: /docs/rfc/0015-database-strategy.mdADR lifecycle
An ADR is not a frozen document, it evolves through a defined lifecycle: a proposal is either accepted or rejected, and an accepted decision eventually leaves the game, either because it became obsolete or because another decision replaced it.
The four statuses
An ADR always carries one of these four statuses: Proposed, Accepted, Deprecated or Superseded. The first two describe a living decision, the last two a decision that has left the game, with a nuance that matters: Deprecated means the subject has disappeared, Superseded means the question was asked again and received another answer. Each tab shows the matching ## Status block, with the metadata to include in it.
Proposed: the decision is under discussion.
The ADR is written but not yet validated. This is the time for reviews, comments and adjustments.
## StatusProposed
Reviewers: @alice, @bobDiscussion: https://github.com/org/repo/pull/123Accepted: the decision is validated and active.
The team commits to respecting this decision. All new code must comply with it.
## StatusAccepted
Accepted by: Architecture BoardDate: 2024-01-15Deprecated: the decision is no longer relevant.
The context has changed, but there is no direct replacement. For example, an entire component has been removed.
## StatusDeprecated
Deprecated: 2024-06-01Reason: Service X has been decommissionedSuperseded: the decision is replaced by a new one.
A new ADR was created with a different decision. The old ADR stays readable for the history.
## StatusSuperseded by ADR-0067
Superseded: 2024-06-15See: docs/adr/0067-migrate-to-cockroachdb.mdWhen should you write an ADR?
Not every decision deserves an ADR. Here is how to identify the ones that do.
The "5 inverted whys" test
If you can answer "yes" to at least 3 of these questions, an ADR is justified:
- Cost of change: will this decision be expensive to change later?
- Team impact: will several people be affected?
- Lifespan: will this decision still be relevant in 2 years?
- Alternatives: have you evaluated several options?
- Risk of misreading: could someone misunderstand this choice?
Concrete examples
The table below applies this test to common decisions. The right-hand column gives the criterion that settled it: that is the one to remember, because your context will flip some rows the other way.
| Decision | ADR needed? | Reason |
|---|---|---|
| Choice of the main language | Yes | Major impact, long lifespan |
| Choice of the web framework | Yes | High cost of change |
| Git branching strategy | Yes | Team impact, possible misreading |
| Name of a variable | No | Negligible impact |
| Choice between two HTTP libraries | Depends on context | Depends on how critical it is |
| Microservices vs monolith architecture | Absolutely | Fundamental decision |
A faster rule of thumb, usable in a meeting without going through the five questions: if a technical choice triggers a discussion of more than thirty minutes, it deserves an ADR. The length of the discussion is a fairly faithful measure of the number of options at stake and of the disagreement on criteria, which is exactly what a future reader will want to find.
Anti-patterns to avoid
These five drifts have one thing in common: they turn a decision tool into a documentation exercise. The shared symptom is that nobody reads the ADRs anymore, and so nobody writes them.
1. The novel ADR
Symptom: an ADR of more than 3 pages.
Problem: nobody will read it. The ADR loses its function as a quick reference.
Solution: one ADR = one decision. If you need more space, you are documenting several decisions.
2. The ADR without alternatives
Symptom: "We chose X" with no mention of Y and Z.
Problem: the reader does not understand why X rather than anything else.
Solution: always document at least 2-3 evaluated options, even if the choice seemed obvious.
## Considered Options
### Option 1: Do nothingKeep the current architecture.**Rejected**: does not solve the scalability problem.
### Option 2: Chosen solution[...]3. The post-mortem ADR
Symptom: an ADR written 6 months after the decision.
Problem: the context is forgotten and the alternatives are no longer clear.
Solution: write the ADR at the time of the decision, even as a draft.
4. The orphan ADR
Symptom: an ADR that is never referenced and not part of the workflow.
Problem: developers do not know it exists.
Solution: build ADRs into the code review process and link them from the READMEs.
5. The over-technical ADR
Symptom: an ADR that is incomprehensible to someone outside the immediate team.
Problem: it loses its value as a knowledge transfer tool.
Solution: a good ADR must be understandable by a developer joining the team.
Tools and integration
An ADR is stored with the code it concerns, in the same repository and under the same review. This proximity is what keeps it up to date, whereas a separate documentation space goes stale within a few months.
Recommended file structure
ADRs live in docs/adr/ at the root of the repository, one Markdown file per decision, plus a template.md that the team copies for each new ADR.
Répertoireproject/
Répertoiredocs/
Répertoireadr/
- 0000-use-adr.md
- 0001-use-typescript.md
- 0002-choose-react-framework.md
- 0003-api-rest-vs-graphql.md
- template.md
Répertoiresrc/
- …
- README.md
The file name follows the NNNN-title-in-kebab-case.md format, the convention established by adr-tools and adopted by most teams. The four-digit number guarantees two things: natural chronological sorting by the file system, and a short, stable reference ("ADR-0042") usable in a code review or a ticket. By convention, the file 0000-use-adr.md is the ADR that decides to use ADRs, which gives the repository its first example.
adr-tools: a CLI to manage your ADRs
adr-tools, by Nat Pryce, is the set of shell scripts that popularised the NNNN-title.md naming convention. It handles numbering, creates the file from a template, and above all automatically updates the status of the replaced ADR when you create a successor with -s, the operation most often forgotten when done by hand.
# Installation (macOS)brew install adr-tools
# Initialise the ADR folderadr init docs/adr
# Create a new ADRadr new "Use PostgreSQL for main database"# Creates docs/adr/0001-use-postgresql-for-main-database.md
# Mark an ADR as supersededadr new -s 1 "Migrate to CockroachDB"# Creates 0002 and updates the status of 0001
# Generate a table of contentsadr generate toc > docs/adr/README.md
# Generate a graph of the linksadr generate graph | dot -Tpng > docs/adr/graph.pngLog4brains: a web interface for ADRs
Log4brains generates a navigable website from your ADRs:
# Installationnpm install -g log4brains
# Initialise in an existing projectlog4brains init
# Preview locallylog4brains preview
# Generate the static sitelog4brains buildThe tool generates a web interface with search, filters by status, and a dependency graph between ADRs.
CI/CD integration
An automatic validation in continuous integration is what keeps the practice from degrading silently: without it, mandatory sections end up missing and the numbering ends up with holes. The workflow below runs only on pull requests that touch docs/adr/, checks for the status, context and decision sections, in English or in French since the examples on the French version of this page mix both, then checks that file numbers follow each other without a gap, starting from the first number present: 0000 or 0001 depending on whether the repository follows the convention of this page or the one of adr init. Each step fails on the first defect, otherwise the check would print its messages without ever blocking the pull request.
Two hardening details deserve attention because they apply to all your workflows: permissions: {} at workflow level removes all rights from the job on the repository, which are then granted back at the strict minimum (here contents: read), and persist-credentials: false stops actions/checkout from leaving the authentication token in the runner's .git/config, where any later step could read it.
name: ADR Validation
on: pull_request: paths: - 'docs/adr/**'
permissions: {}
jobs: validate: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: persist-credentials: false
- name: Validate ADR format run: | status=0 for file in docs/adr/[0-9]*.md; do # Mandatory sections, English or French heading grep -qE "^## (Status|Statut)" "$file" || { echo "Missing Status in $file"; status=1; } grep -qE "^## (Context|Contexte)" "$file" || { echo "Missing Context in $file"; status=1; } grep -qE "^## (Decision|Décision)" "$file" || { echo "Missing Decision in $file"; status=1; } done exit $status
- name: Check sequential numbering run: | status=0 expected="" for file in docs/adr/[0-9]*.md; do # 10# forces base 10: 0042 is 42, 0000 is 0 num=$((10#$(basename "$file" | cut -d'-' -f1))) expected=${expected:-$num} if [ "$num" -ne "$expected" ]; then echo "Gap in numbering: expected $expected, got $num" status=1 fi expected=$((num + 1)) done exit $statusComplete example: a real ADR
This ADR, taken from a real and anonymised project, documents the adoption of Event Sourcing on a business domain. It is valuable above all for what it accepts: four weeks of training, two migration sprints and an extra piece of infrastructure to run. An ADR whose negative consequences section is empty is not an ADR, it is a sales pitch. The Required actions section, absent from the standard MADR template, is a common and useful addition: it turns the decision into tracked tasks.
# ADR-0015: Use Event Sourcing for the Orders domain
## Status
Accepted
Date: 2024-02-20Authors: Marie Dupont, Jean MartinReviewers: Architecture Team
## Context and Problem Statement
The current order management system uses a classic CRUD model.We face several problems:
1. **Insufficient audit**: impossible to trace the full history of an order2. **Update conflicts**: concurrent changes create inconsistencies3. **Limited business analysis**: historical data is lost after an update4. **GDPR compliance**: difficulty proving the history of consents
The current volume is 50,000 orders/day, projected at 200,000 in 18 months.
## Decision Drivers
* Full traceability of every change* Read performance (real-time dashboards)* "Replay" capability to correct errors* SOC 2 audit compliance
## Considered Options
### Option 1: Add an audit table
Keep the CRUD model and add an `order_audit` table with triggers.
**Pros**:- Minimal change to the existing code- Team familiar with the approach
**Cons**:- Does not solve concurrency conflicts- Trigger performance at scale- Partial audit (not every change is captured)
### Option 2: Event Sourcing (chosen)
Store each change as an immutable event.Rebuild the current state by projecting the events.
**Pros**:- Full traceability by design- Solves concurrency (append-only)- Allows replay and multiple projections- Fits CQRS patterns
**Cons**:- Learning curve (pattern little known to the team)- Complexity of event schema migrations- Need for an event store (new infrastructure)
### Option 3: Temporal tables (SQL Server)
Use the native temporal tables of SQL Server.
**Pros**:- Built into the DBMS- No architecture change
**Cons**:- Vendor lock-in- No support for custom projections- Limited to SQL queries
## Decision
**We adopt Event Sourcing for the Orders domain.**
The implementation will use:- EventStoreDB as the event store (open source, optimised for this pattern)- Projections to PostgreSQL for reads- CloudEvents format for the event structure
## Consequences
### Positive
* Native audit: every change is a timestamped event* Concurrency resolved through aggregate versioning* New possibilities: replay, what-if analysis, temporal debugging* Solid base for future analytics features
### Negative
* 4 weeks of team training (Event Sourcing workshop)* 2 sprints to migrate the existing Orders domain* New infrastructure: EventStoreDB cluster to provision* Increased complexity for junior developers
### Required actions
1. [ ] Team training (W1-W2)2. [ ] EventStoreDB provisioning (W2)3. [ ] Gradual migration of the Orders domain (W3-W6)4. [ ] Documentation of the patterns for other domains
## Links
* RFC: /docs/rfc/0008-event-sourcing-strategy.md* Benchmark: /docs/benchmarks/eventstore-performance.md* Training: https://internal-wiki/event-sourcing-workshopChecklist before approval
Moving to the Accepted status is irreversible: the ADR becomes immutable, and any later correction will require creating another one. This few-minute review is therefore the last moment when the document can be fixed. The eight points below cover the defects found most often in reviews.
- Title: actionable and clear ("Use X for Y")
- Context: the problem is understandable without external context
- Alternatives: at least 2 options documented
- Decision: clearly identifiable within 30 seconds
- Consequences: positive AND negative listed
- Length: less than 2 printed pages
- Audience: understandable by a new team member
- Links: references to related documents where needed
Key takeaways
- An ADR captures a single decision, its context and its consequences, in one or two pages: it answers "why this choice?", not "how does it work?".
- It is stored next to the code, in
docs/adr/NNNN-title.md, and goes through the same review as the code it concerns. - MADR adds to Nygard's skeleton the decision drivers, written before comparing, and the rejected options, which are the information missing three years later.
- An accepted ADR is never modified again: if the context changes, you write a new one, and the old one becomes
Superseded. - An ADR is justified when the choice is costly to undo; a discussion of more than thirty minutes is a good signal.
- The negative consequences are part of the decision: an empty section signals a sales pitch, not an ADR.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
A new full quiz with random questions
Work again only on the questions you missed
External references
These five sources are where the format, the template and the tools cited in this lesson come from: Nygard's article for the origin, MADR for the enriched template, the two tools for the implementation, and the ADR organisation on GitHub for examples.
- Documenting Architecture Decisions, Michael Nygard's founding article
- MADR Template, Enriched Markdown template
- adr-tools, CLI to manage ADRs
- Log4brains, ADR site generator
- ADR GitHub Organization, Resources and examples