Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français
Lire en français

Architecture Decision Records (ADR): tracing your choices

32 min read
Improve this page

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

AspectClassic documentationADR
LengthDozens of pages1-2 pages maximum
ScopeThe whole systemA single decision
UpdatesRare (often outdated)Immutable (you create a new ADR)
Question"How does it work?""Why this choice?"
AudienceNewcomersFuture decision makers
LifespanShort (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:

  1. An architect makes an important technical choice
  2. They talk about it in a meeting and send an email
  3. 18 months later, they leave the company
  4. A new developer wonders "why PostgreSQL and not MongoDB?"
  5. Nobody remembers, the email cannot be found
  6. The team spends weeks analysing and benchmarking
  7. They make a decision... maybe the same one, maybe a different one
  8. The cycle starts again

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.

Without an ADR: a technical choice, discussed in a meeting or by email that quickly becomes untraceable, turns into a question again eighteen months later, triggers an investigation of several weeks and a redone decision, and the cycle starts again. With an ADR: the same choice, an ADR written in thirty minutes, the same question eighteen months later, five minutes of reading, then either a validated context or a new ADR.Without an ADRthe cycle starts overTechnical choiceMeeting, e-mailsoon lost18 months on:“why PostgreSQL?”Investigation, benchmarksseveral weeksDecision made againWith an ADRTechnical choiceADR written30 minutes18 months on:“why PostgreSQL?”Reading the ADR5 minutesContext confirmedor new ADR
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

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.

BenefitWhat changes in practice
Faster onboardingThe newcomer reads ten dated decisions instead of questioning five people
Faster decisionsOptions already evaluated are not re-analysed from scratch
Fewer "why?" questionsInterruptions of senior developers become rarer
Easier auditsThe 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.

Anatomy of an ADR in MADR format, on the example ADR-0042 (PostgreSQL as the main database, status Accepted): context, decision drivers (performance, skills, licence cost, support), options considered (MySQL rejected for lack of JSON support, PostgreSQL chosen, MongoDB rejected for lack of ACID transactions), decision, then positive consequences and points to watch.Considered optionsMySQL: rejectedlimited JSON supportPostgreSQL: chosenJSON, performance, open sourceMongoDB: rejectedno ACID transactionsADR-0042: PostgreSQL as the main databaseStatus Accepted · 2024-01-15 · Backend teamContextwhy decide, which problem, which constraintsDecision driversperformance, team skills, licence cost, supportDecisionPostgreSQL 16+ for new applicationsConsequencesgood: performance, training · to watch: migration, DBA
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

Nygard template (original)

The original template is minimalist, and that is its strength:

docs/adr/0001-record-architecture-decisions.md
# 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.

  1. Title: numbered, with a clear action ("Use X for Y")
  2. Date: when the decision was made
  3. Status: Proposed, Accepted, Deprecated or Superseded
  4. Context: why this decision is needed
  5. Decision: what was decided
  6. 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.

docs/adr/0042-use-postgresql-for-main-database.md
# 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 cases
2. JSONB support lets us mix relational and document models
3. 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.md

ADR 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.

ADR lifecycle: Proposed becomes Accepted if approved or Rejected otherwise; an accepted ADR becomes Deprecated when it is obsolete, or Superseded when a new ADR replaces it and references it.approvedrejectedreplaced by ADR-XXXobsoleteProposeddecision under discussionAcceptedvalidated and activeRejectedabandonedSupersededreplacedDeprecatedobsoleteNew ADRreferenced
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

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.

## Status
Proposed
Reviewers: @alice, @bob
Discussion: https://github.com/org/repo/pull/123

When 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:

  1. Cost of change: will this decision be expensive to change later?
  2. Team impact: will several people be affected?
  3. Lifespan: will this decision still be relevant in 2 years?
  4. Alternatives: have you evaluated several options?
  5. 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.

DecisionADR needed?Reason
Choice of the main languageYesMajor impact, long lifespan
Choice of the web frameworkYesHigh cost of change
Git branching strategyYesTeam impact, possible misreading
Name of a variableNoNegligible impact
Choice between two HTTP librariesDepends on contextDepends on how critical it is
Microservices vs monolith architectureAbsolutelyFundamental 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 nothing
Keep 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.

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 and usage
# Installation (macOS)
brew install adr-tools
# Initialise the ADR folder
adr init docs/adr
# Create a new ADR
adr new "Use PostgreSQL for main database"
# Creates docs/adr/0001-use-postgresql-for-main-database.md
# Mark an ADR as superseded
adr new -s 1 "Migrate to CockroachDB"
# Creates 0002 and updates the status of 0001
# Generate a table of contents
adr generate toc > docs/adr/README.md
# Generate a graph of the links
adr generate graph | dot -Tpng > docs/adr/graph.png

Log4brains: a web interface for ADRs

Log4brains generates a navigable website from your ADRs:

Installation and usage
# Installation
npm install -g log4brains
# Initialise in an existing project
log4brains init
# Preview locally
log4brains preview
# Generate the static site
log4brains build

The 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.

.github/workflows/adr-check.yml
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 $status

Complete 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.

docs/adr/0015-use-event-sourcing-for-orders.md
# ADR-0015: Use Event Sourcing for the Orders domain
## Status
Accepted
Date: 2024-02-20
Authors: Marie Dupont, Jean Martin
Reviewers: 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 order
2. **Update conflicts**: concurrent changes create inconsistencies
3. **Limited business analysis**: historical data is lost after an update
4. **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-workshop

Checklist 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

6 questions
6 min
70% to pass

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

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.

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn