Be Software Architecture Wise
Be Wise Apps

Leer en español · Lire en français · Auf Deutsch lesen

Section 4.5 of 4.8 · Intermediate · 22 min read

Architecture Decision Records

Video lesson

5 min · narrated, with captions
Listen only (audio)
Read the transcript

Diagrams show how a system is built, but rarely why. In this lesson you'll learn how to record significant decisions so that their reasons survive, long after the people who made them have moved on.

Imagine moving into a house and finding a wall that seems to serve no purpose. Do you knock it down? If the builder had left a note saying this wall holds up the bathroom upstairs, you would know. A decision record is that note for software. And notes are never rewritten. If the owners change their minds, they add a new note saying what changed, and why.

An Architecture Decision Record, or ADR, is a short text document that captures one significant decision: the situation that forced it, the options weighed, what was chosen, and what follows from the choice. Kept in order, the records form the system's decision log. The lightweight form comes from a 2011 blog post by Michael Nygard. His problem was the newcomer who finds a strange choice in the code. Without the reasons, they either accept it blindly, or change it blindly and break something whose reasons still hold.

Nygard proposed five parts. The title names the decision, not the problem, with a sequence number that is never reused. The context describes the forces at play in neutral language: facts, not the preferred answer. Include numbers where they exist. The decision is written in full sentences in the active voice, beginning we will, so there's no doubt about what was decided. The status shows whether the record is in force. And the consequences list everything that follows: what becomes easier, what becomes harder, and new risks. Richer templates, such as Markdown Architectural Decision Records, also list the options considered, which is often what later readers most want to know.

Records move through a small lifecycle. A record starts as proposed, usually as a pull request, so the discussion happens in review comments. When the people accountable agree, it is merged as accepted, and its text is frozen. A proposal that is turned down is kept as rejected. That's valuable: the next person who suggests the same idea finds the earlier reasoning. Later, an accepted record may be deprecated, because it no longer applies, or superseded by a new record, with links in both directions. The central rule: only the status line and links of an accepted record change. Editing it would turn history into fiction. New thinking goes in a new record.

Here's a complete record for the food-delivery platform. The context gives numbers, not adjectives. At the Friday peak, the ninety ninth percentile order confirmation time reaches four point two seconds against a target of one point five. And one outage blocked all orders for twenty three minutes. Next come the decision drivers, stated before any option is judged. Confirmation must not depend on other services, and no order event may be lost. Three options are given a fair hearing: keep the calls and add retries, publish events through a managed broker, or let consumers poll a shared table. The decision is option two. But payment authorisation stays synchronous, because the customer must see the result. The consequences are honest. Confirmation no longer waits, but services become eventually consistent, there's a broker to pay for, and consumers must handle duplicate events. Finally, confirmation says how the team will check that the decision really holds: a load test, and a build check.

Not every choice needs a record. Write one when a decision is expensive to reverse, or affects a key quality such as availability or cost. Write one when it constrains other teams, or was contested, so the reasoning isn't argued again from scratch. Choices that are local and cheap to reverse, like a test library, don't need one. A good signal: if someone will ask why on earth did they do this within a year, write it now. Keep records in the code repository as numbered Markdown files, reviewed in pull requests, with a short index that a newcomer can read in five minutes.

Records fail in recognisable ways. The first is the record written after the fact, reverse-engineered to look rational. Write it while deciding, as a proposal. Another lists no real alternatives, or adds a straw-man option that could never work, so the favourite looks vetted. Another lists only benefits. Every significant decision has costs, and readers stop trusting a log that hides them. And the last is editing accepted records to match the current design, which destroys the very history the log exists to keep.

The full section shows the complete record, the one-sentence Y-statement, and how teams review decisions. Next, in Section four point six, we look at arc forty-two and other templates for the whole description. See you there.

What you need to know

What an ADR is and the problem it solves

An Architecture Decision Record (ADR) is a short text document that captures one architecturally significant decision: the situation that forced it, the options that were weighed, what was chosen and what follows from the choice. A collection of them, kept in order, forms the decision log of a system. The idea of recording decisions is old (design rationale research goes back decades, and Jeff Tyree and Art Akerman published a detailed decision template in IEEE Software in 2005), but the lightweight form most teams use today comes from Michael Nygard's blog post Documenting Architecture Decisions, published on 15 November 2011.

Nygard's starting point was a problem every long-lived team recognises. A new developer finds a decision in the code that looks strange: an extra queue, a duplicated table, a library nobody would choose today. Without knowing why it was made, the newcomer has two bad choices. They can accept it blindly, preserving something whose reasons may no longer apply, or change it blindly, breaking something whose reasons still do. Nygard's remedy was to write down the reasons at the time the decision is made, in a small file kept with the code, so that the motivation travels with the system. Big architecture documents rarely achieve this because nobody updates them; a short record of one decision, written once and never rewritten, stays true because it only claims to describe what was known and decided on a particular date.

This builds directly on the anatomy of a decision in Chapter 1: decisions and trade-offs: context, options, criteria, decision with rationale, consequences and status. An ADR is simply that anatomy written down in a predictable format. It also answers the question architecture versus design leaves open in practice: if architecture is the set of significant decisions, the ADR log is the closest thing a team has to a list of its architecture.

Nygard's format

Nygard proposed five parts. Most teams still use them, often moving the status to the top so that readers see at once whether the record is in force. His guidance on how to write each part matters as much as the headings.

The five parts of a Nygard-style ADR

PartWhat it containsHow to write it well
TitleA short noun phrase naming the decision, with a sequence number, such as ADR 12: Order events through a message broker.Name the decision, not the problem: a reader scanning the log should learn what was decided from the title alone.
ContextThe forces at play: the problem, the relevant quality requirements and constraints, technical and organisational facts, and the tension between them.Nygard asks for value-neutral language: describe facts and forces, not the preferred answer. Include numbers where they exist (peak load, targets, incident dates).
DecisionThe response to those forces.Full sentences in the active voice, beginning We will..., so that there is no doubt about what was decided and by whom.
StatusProposed, accepted, and later possibly deprecated or superseded, with a reference to the replacing record.Keep it current; it is the one field that changes after acceptance.
ConsequencesThe resulting context after the decision is applied.List all consequences, not only the positive ones: what becomes easier, what becomes harder, new risks, new work and options that are now closed.

Nygard also suggested that a record should fit on one or two pages, that records are numbered sequentially and numbers are never reused, and that they live in the project repository as plain text. Each of those small rules exists for a reason: short records get read, stable numbers can be cited in code comments and commit messages, and plain text in the repository can be reviewed and versioned like code.

Other templates: MADR, Y-statements and richer forms

Nygard's format says nothing explicit about the options that were rejected, and many teams found that the alternatives are exactly what a later reader most wants to know. Several templates add them.

MADR (Markdown Architectural Decision Records, maintained at adr.github.io) is the most widely used structured template. Its current release at the time of writing is version 4.0.0, published in September 2024. The full template has a title, then Context and Problem Statement, optional Decision Drivers, Considered Options, Decision Outcome with optional Consequences and Confirmation subsections, optional Pros and Cons of the Options and optional More Information. Metadata such as status, date, decision-makers, and who was consulted and informed can go in a YAML header. Two ideas in MADR are worth borrowing even if you use another format. Decision drivers make the criteria explicit before the options are judged, which is the weighted-matrix discipline from Chapter 1 in compact form. Confirmation says how the team will check that the decision is actually implemented and still holds, for example a review, a test or an automated architecture check.

The Y-statement, proposed by Olaf Zimmermann and colleagues and first presented at the SATURN conference in 2012, compresses a decision into one structured sentence: In the context of (a use case or component), facing (a concern or quality requirement), we decided for (the option) and neglected (the alternatives), to achieve (the benefits), accepting that (the drawbacks). The name is a pun on why. A Y-statement is ideal for a decision log summary, a slide, a code comment or a pull request description; for a contested one-way door it is usually too compressed, and teams expand it into a full record.

Heavier templates exist for regulated or very large settings. Tyree and Akerman's 2005 template adds fields such as assumptions, constraints, related decisions, related requirements and implications. These are useful when decisions must be traced to requirements for an auditor; for most product teams they add effort that nobody reads.

Choosing a template

TemplateSizeRecords rejected options?Best for
Nygard (2011)Half a page to two pagesOnly if you put them in the context or consequencesTeams starting out; the smallest habit that works.
MADR 4.0.0 (2024)One to three pagesYes: considered options, with optional pros and cons of eachMost product teams; contested decisions where alternatives must be visible.
Y-statement (2012)One sentenceYes, in the neglected clauseDecision log summaries, small decisions, pull request descriptions, slides.
Tyree and Akerman (2005)Several pagesYes, plus assumptions, constraints and traceabilityRegulated environments needing traceability to requirements.

Status, lifecycle and why accepted records are not edited

An ADR moves through a small number of states. It starts as proposed, usually as a pull request that adds the file, so that discussion happens in review comments. When the people accountable agree, it is merged as accepted. A proposal that is turned down can be kept as rejected, which is valuable: the next person who suggests the same idea finds the earlier reasoning. Later, an accepted record may become deprecated (the decision no longer applies, perhaps because the component was retired) or superseded by a newer record that replaces it, with links in both directions. MADR lists the same set: proposed, rejected, accepted, deprecated and superseded.

The central rule is that an accepted record's content is not rewritten. If the team changes its mind, it writes a new ADR that supersedes the old one, and changes only the old record's status line to point forward. The reasons are practical. First, the record is evidence of what was known and decided at a point in time; editing it after the fact turns history into fiction and destroys the trust that makes the log useful. Second, the old record explains why the system looked the way it did for a period, which matters when debugging old data or reading old code. Third, a new record forces the team to state what changed in the context, which is often the most important insight. Small corrections (a typo, a broken link) are harmless; changing the decision, the reasons or the consequences is not. Martin Fowler's description of the practice says the same: once accepted, a record is not reopened; a superseding record is written instead.

The ADR lifecycle

The lifecycle of an Architecture Decision Record: proposed records are accepted or rejected; accepted records may later be deprecated or superseded by a new record, but their text is never rewrittenProposedopen pull requestreview agreesAcceptedmerged; text frozenreview declinesRejectedkept for the recordSupersededby ADR-0031replacedDeprecatedno longer appliesretiredADR-0031new recordsupersedesOnly the status line and linksof an accepted record change.New thinking goes in a new ADR.
Records are proposed and reviewed, then accepted or rejected; accepted records are later deprecated or superseded, never rewritten.

Where to keep ADRs

Keep ADRs in the repository of the code they govern, as plain-text files (usually Markdown), one decision per file, with a sequence number and a short slug in the file name. Two directory conventions are common: doc/adr, the default of Nat Pryce's adr-tools command-line tool, and docs/decisions, recommended by MADR with names such as 0012-publish-order-events-through-broker.md. Thoughtworks placed lightweight ADRs in the Adopt ring of its Technology Radar (most recently in 2018) and specifically recommended source control over a wiki, so that records stay in step with the code they describe.

Keeping records next to the code has concrete advantages. They are reviewed in the same pull requests as the code that implements them, they are versioned and branched with it, a developer can find them with the same search tools, and code can point at them: a comment such as see ADR-0012 above an outbox relay tells the next reader where the reasons are. Decisions that span many repositories, such as an organisation-wide choice of identity provider, need a home of their own; a dedicated architecture repository with the same conventions works well, with links from each affected codebase.

docs/
  decisions/
    README.md                                  <- the decision log (index)
    0001-record-architecture-decisions.md
    0004-use-hosted-card-fields.md
    0007-use-managed-postgresql-for-orders.md
    0012-publish-order-events-through-broker.md
    template.md
src/
  ordering/
    outbox_relay.py      # comment: see docs/decisions/0012-...
A typical layout: numbered ADR files beside the code, an index, a template and a code comment that points back to the record

Which decisions deserve an ADR

Writing an ADR for every choice buries the important ones; writing none loses the reasons. The test is significance, as defined in Chapter 1: a decision deserves a record when it is expensive to reverse, affects a key quality attribute, has wide scope, constrains other teams, or was contested. In practice that means choices such as a primary datastore, a communication style between services, an identity provider, an API versioning policy, a hosting platform, a build-versus-buy decision, and conventions whose violation is expensive (how money is represented, how time zones are stored). It also includes decisions not to do something that people keep suggesting, such as not introducing a cache yet.

Choices that are local, cheap to reverse and invisible outside one module usually do not need a record: the choice of a test assertion library, the name of a class, the internal layout of a component. A useful signal is the question a newcomer would ask: if someone is likely to ask why on earth did they do this? within a year, write the record now, while the reasons are fresh.

Decision guide

Decision guide: does this decision need an ADR?

Ask these questions in order when a technical choice comes up in design discussion or code review.

  1. Would reversing this decision in a year take more than a few days of work, a data migration or changes by other teams?
    YesWrite an ADR. It is a one-way door or close to one.
    NoGo to the next question.
  2. Does it noticeably affect a quality attribute that matters to stakeholders (for example availability, latency, security or cost) or a constraint such as regulation?
    YesWrite an ADR, and link it to the quality scenario it serves.
    NoGo to the next question.
  3. Was it contested, or did the team reject an option that people are likely to propose again?
    YesWrite a short ADR (a Y-statement may be enough) so the reasoning is not re-argued from scratch.
    NoGo to the next question.
  4. Will a newcomer probably look at the result and ask why it was done this way?
    YesWrite a short ADR or at least a code comment and a commit message that explain the reason.
    NoNo ADR needed. A clear commit message and the code itself are enough.

A complete worked ADR

The food-delivery platform used throughout this chapter has customer, restaurant and courier apps, an ordering service, payments through an external payment provider, courier dispatch and notifications. The record below follows the MADR structure, slightly shortened. Notice how the context gives numbers and dates rather than adjectives, how the drivers are stated before the options, how the rejected options are given a fair hearing, and how the consequences include real costs. The decision itself (asynchronous messaging for order events) is explored properly in Chapter 6: event-driven architecture; here the point is how it is recorded.

# ADR-0012: Publish order events through a managed message broker

- Status: Accepted
- Date: 2026-09-14
- Deciders: ordering team lead, platform lead, principal architect
- Consulted: restaurant, courier and notifications teams; SRE
- Related: ADR-0007 (managed PostgreSQL for the order service)

## Context and problem statement

When a customer's payment succeeds, the ordering service calls the restaurant,
courier-dispatch and notification services synchronously over HTTP before it
confirms the order. On Friday evenings (peak about 40 orders/s) p99 order
confirmation time reaches 4.2 s against our target of 1.5 s (scenario QS-3).
On Friday 21 August a notification-service outage blocked all new orders for
23 minutes. Two new consumers (loyalty points, analytics) want order data.

## Decision drivers

- Order confirmation must not depend on notification or dispatch availability.
- p99 confirmation under 1.5 s at three times today's peak.
- No order event may be lost, even if a service crashes mid-request.
- New consumers should not require changes to the ordering service.
- The platform team has three people; operational load must stay small.

## Considered options

1. Keep synchronous calls; add timeouts, retries and circuit breakers.
2. Publish events to a managed message broker using a transactional outbox.
3. Let consumers poll a shared "order_events" table in the order database.

## Decision outcome

Chosen option: 2. We will publish OrderPlaced, OrderAccepted, OrderPickedUp,
OrderDelivered and OrderCancelled events to our cloud provider's managed
broker. The ordering service writes each event to an outbox table in the
same database transaction as the order; a relay publishes it. Payment
authorisation stays synchronous, because the customer must see the result.

### Consequences

- Good: confirmation no longer waits for downstream services.
- Good: loyalty and analytics can subscribe without ordering-team work.
- Bad: services become eventually consistent; the restaurant tablet may
  show a new order a second or two after the customer's confirmation.
- Bad: a broker to pay for and monitor; consumers must be idempotent
  because delivery is at-least-once.
- Neutral: event schemas become public contracts and need versioning.

### Confirmation

Load test at 120 orders/s in staging before go-live; a build check fails
if the ordering service gains an HTTP client for notification or dispatch.

## Pros and cons of the other options

- Option 1: least new infrastructure, but a slow dependency still delays
  every order, and each new consumer means another call to add.
- Option 3: no new infrastructure, but couples every consumer to our schema
  and adds polling load to the order database (see ADR-0007).
Markdown source of ADR-0012 in docs/decisions/0012-publish-order-events-through-broker.md

Worked example

Worked example: the same decision as a Y-statement

For the decision log, a pull request description or a slide, the team also writes the decision as one Y-statement.

Y-statementIn the context of order confirmation in the food-delivery platform, facing slow or unavailable downstream services that block orders at peak times, we decided for publishing order events through a managed message broker with a transactional outbox and neglected synchronous calls with circuit breakers, and consumers polling a shared table, to achieve order confirmation that does not depend on notification, dispatch or restaurant services, and new consumers that need no change to ordering, accepting that services become eventually consistent, the broker must be run and paid for, and every consumer must handle duplicate events.
What it keepsThe context, the driving concern, the choice, the rejected options, the benefits and the accepted costs: every element of the full record, at a glance.
What it losesThe numbers (p99 of 4.2 s, 23-minute outage), the decision-makers, the confirmation plan and the reasoning against each rejected option. For a decision this significant, the full record is still needed.

A good test of any ADR is whether you can compress it into a sensible Y-statement. If you cannot fill the neglected or accepting that clauses, the record probably considered no real alternatives or hid its costs.

Reviewing ADRs

Because an ADR is a file in the repository, the natural review mechanism is the pull request. The proposer opens it with status proposed; the people affected comment; the record is revised; when the accountable people agree, the status changes to accepted and it is merged. This keeps the discussion attached to the record and visible to anyone who later reads the history.

Who must review depends on how decisions are governed. In a centralised model an architecture board approves significant records. In the advice process described by Andrew Harmel-Law (martinfowler.com, 2021), anyone may make an architectural decision, provided they first seek advice from everyone meaningfully affected and from people with relevant expertise, and record that advice; the decision-maker need not follow the advice but must hear it. ADRs are the backbone of that process: the advice appears in the record, and a regular open advisory forum reviews proposed records. Governance models are covered in Chapter 11: governance in agile organisations.

Olaf Zimmermann's ecADR checklist (2020) gives reviewers a compact definition of done for a decision. Evidence: there is reason to believe the option will work, from a spike, a proof of concept or trusted experience. Criteria: at least two options were compared against the stakeholder concerns. Agreement: the decision has been challenged by peers and the team accepts it. Documentation: it is recorded and shared with those affected. Realisation and review: implementation is planned and a date or trigger for revisiting it is set. A reviewer who checks those five points, plus the honesty of the consequences, catches most weak records.

Anti-patterns

ADRs fail in recognisable ways. Zimmermann catalogued several in 2023 with memorable names, and the most damaging are worth knowing.

The decision log as an index

Individual records are useful; the log as a whole is more useful. A short index file (often the README.md of the decisions folder) lists every record with its number, title, status and date. A newcomer reads it in five minutes and gets a compressed history of the architecture: what was decided, what was rejected, what was replaced and when. Tools such as adr-tools can generate the index and create superseding records that update the old record's status automatically, and documentation tools including the Structurizr DSL can import a folder of ADRs into the published documentation.

Treat the log as part of the architecture description. The arc42 template has a section for architecture decisions that can simply link to it, and a docs-as-code pipeline can publish it with the rest of the documentation.

# Decision log

| ADR  | Title                                                | Status                 | Date       |
|------|------------------------------------------------------|------------------------|------------|
| 0001 | Record architecture decisions                        | Accepted               | 2025-03-02 |
| 0004 | Use the payment provider's hosted card fields        | Accepted               | 2025-04-18 |
| 0007 | Use managed PostgreSQL for the order service         | Accepted               | 2025-06-11 |
| 0009 | Send push notifications through a third-party SDK    | Superseded by 0015     | 2025-09-30 |
| 0012 | Publish order events through a managed broker        | Accepted               | 2026-09-14 |
| 0013 | Store courier locations in a time-series database    | Rejected               | 2026-09-20 |
| 0015 | Run our own notification service on the broker       | Accepted               | 2026-10-08 |
docs/decisions/README.md: the decision log for the food-delivery platform

Check your understanding

Why should the Context section of an ADR avoid arguing for the chosen option?

Because its job is to record the forces and facts as they were, so that a later reader can judge whether they still hold. If the context is written as advocacy, the reader cannot separate the situation from the conclusion.

Your team changes its mind about an accepted decision. What do you do with the old ADR?

Leave its content unchanged, set its status to superseded with a link to the new record, and write a new ADR that explains what changed in the context and what is now decided.

What does MADR's Confirmation section add that Nygard's format lacks?

A statement of how the team will check that the decision is implemented and still holds, such as a load test, a review or an automated dependency check.

What is a quick test of whether an ADR genuinely considered alternatives and costs?

Try to write it as a Y-statement. If the "and neglected" or "accepting that" clauses cannot be filled honestly, the record is missing real options or real consequences.

Next · 4.6arc42 and templatesThe twelve sections of arc42, what goes in each and how much to write for small and large systems, a one-page arc42-lite example, the SEI and IEEE 1016 templates, and conformance with ISO/IEC/IEEE 42010.Continue →

Sources and further reading

Have a question about this section? ArchitectBot, the course’s AI study assistant, answers from the course first.

Ask: “We have a two-year-old system and no ADRs. Should we write records for the decisions already made, and if so, how do we avoid writing after-the-fact justifications?”