Levelwise
English
Architecture and system design

Architecture Decision Records (ADR)

Write each important architecture decision in a short document. Write why it was needed, what you chose and what it costs. Keep the document next to the code and never delete it. If the decision changes, write a new document.

Not reviewedWritten with AI helpReading time: 11 minOnline shop exampleSample document and folder structure

Author: bezzad

The problem: “Why did we choose this?”

Two years ago, our online shop team chose RabbitMQ for order events. Today a new programmer asks: “Why not Kafka? Everyone uses Kafka.”

Nobody knows the exact answer. The two people who made that decision have left the company.

timeDecision meetingEveryone knows whyMonth oneTwo people leaveOnly they knew whyYear oneNew programmer"Why did we pick this?"Year twoTwo bad waysBlind copyingor change for no reasonCode shows "what" we built. It does not show "why"
The reason for a decision is in people's heads. People leave, and the reason leaves with them.

Now the team has two bad ways:

  1. Blind copying. “There must have been a reason, let’s not touch it.” Even if the situation has changed and the decision is no longer right.
  2. Change for no reason. “This is old, let’s change it.” Maybe there was an important reason that is still true. The team works for a few months and reaches the same old problem.

Both ways come from one gap: the reason for the decision is not written anywhere.

The idea: a short document for each decision

An Architecture Decision Record (ADR) is a short text file. We write one file for each important decision. Michael Nygard proposed this idea in 2011, and now it is common in many teams.

The features of a good ADR:

  1. It is short. One or two pages. If writing it takes a day, nobody writes it.
  2. It is next to the code. In the same git repository, not in a wiki that nobody finds.
  3. It has one decision. Each file has only one decision.
  4. It has a number. The numbers are in sequence and are never used again.
  5. It does not change. If the decision changes, we write a new ADR.

What parts does an ADR have?

Nygard’s original template has five parts. There are other templates too, but almost all of them have the same idea.

0004 - Use RabbitMQ for order eventsStatusAcceptedContextDecisionConsequencesWhat decision? One sentenceProposed, accepted, supersededWhy must we decide?Limits and optionsWhat did we choose?What gets better and worse?Write the cost too
  1. Title. The decision itself, in one short sentence. “Use RabbitMQ for order events”.
  2. Status. Proposed, accepted, rejected or superseded.
  3. Context. What problem do we have? What limits? What options did we look at? This is the most important part. Without it, the reader does not understand if the decision is still right.
  4. Decision. What we chose. In clear, active sentences: “We will use …”.
  5. Consequences. What gets easier and what gets harder. Every decision has a cost. If you wrote no bad consequences, you probably did not think well.

A full example in the shop

This is the same decision from two years ago, if it had been written that day. ADR text is usually written in English with Markdown:

# 4. Use RabbitMQ for order events

Date: 2024-03-12
Status: Accepted
Deciders: Sara (lead), Reza, Mina

## Context

- Order, Warehouse and Shipping services must react to order events.
- Peak load is about 200 orders per minute.
- The team has 4 developers. Two already run RabbitMQ in production.
- We do not need to replay old events. Each event is handled once.
- Options we looked at: RabbitMQ, Kafka, Azure Service Bus.

## Decision

We will use RabbitMQ (self-hosted) for all order events.

## Consequences

- Good: the team already knows how to run and monitor it.
- Good: simple queues and retries are enough for our load.
- Bad: we cannot replay old events. If we need event replay
  or analytics on the event stream, we must revisit this decision.
- Bad: one more server for the ops team to patch and back up.

Two years later, the new programmer reads this file and gets the answer:

  1. They understand the reason. The team was small, the load was low and event replay was not needed.
  2. They understand when the decision must change. The document itself says: “If event replay is needed, review it again.”
  3. They no longer guess. If today the team is bigger and event analytics is needed, the situation has changed. So the change has a clear reason.

Lifecycle: do not delete an ADR

A year later, the data analytics team needs the old events. The team decides to move to Kafka. We do not change ADR number 4. We write a new ADR with number 9.

ProposalProposedAcceptedAcceptedReplacedSuperseded by 0009Rejected0009 - Move to KafkaNew decisionWe do not change the old text.We only add the status and a link to the new one.
The old document is history. It shows why that decision was right at that time, in that situation.

Why do we not change the old text?

  1. The history stays. Someone who reads the old code understands why it had this shape that day.
  2. Mistakes are not repeated. If the new decision also fails, you can see which options were tried before.
  3. There is more trust. When you know documents do not change silently later, you rely on them.

In ADR number 4 we only change the status and add a link to ADR number 9. ADR number 9 also links to number 4 in its context.

Where and how?

One folder in the repository is enough:

docs/
  adr/
    0001-record-architecture-decisions.md
    0002-use-postgresql-for-orders.md
    0003-modular-monolith-first.md
    0004-use-rabbitmq-for-order-events.md

A simple process that fits the team’s daily work:

  1. Propose in a Pull Request. The person who proposes the decision creates the file with the status “Proposed”.
  2. Discuss in the same Pull Request. The team gives comments, like a Code Review. Other options are added to the context section.
  3. Merge means acceptance. When the Pull Request is merged, the status is “Accepted”.
  4. Code and decision together. If you can, bring the ADR in the same Pull Request that changes the code.
The first ADR: Many teams write the first ADR about ADRs themselves: “We record architecture decisions with ADRs.” This tells new people what this folder is.

Which decisions need an ADR?

Not all decisions need an ADR. The name of a variable or a choice between two small libraries does not need an ADR. Ask a simple question: if this decision is wrong, is changing it expensive?

Write it

  • Choosing the database, the message queue or the main framework.
  • The architecture style, like a modular Monolith or Microservices.
  • The authentication method between services.
  • A rule all teams must follow, like “no service reads another service’s database”.
  • A decision that caused a lot of debate.

Not needed

  • Class names and file locations.
  • Choosing a small library that is easy to change.
  • Something that a coding rule or a Linter already decides.
  • Something that is not a decision, like meeting notes.

Common mistakes

Mistake Result The right way
Writing the ADR months later The real reasons are forgotten. The document only repeats the decision. On the day of the decision, or before it.
An empty or one-line context The reader does not know if the decision is still valid. Write the limits, numbers and options.
Only good consequences It looks like an ad and destroys trust. Write the costs and risks honestly.
Editing an old ADR The history and the earlier reasons are lost. A new ADR that replaces the old one.
A twenty-page document Nobody writes it and nobody reads it. One or two pages. Link to the details.
Keeping it in a place far from the code Nobody finds it and it does not stay in sync with the code. A folder in the same repository.

Summary in five lines

  1. Code shows “what” we built. An ADR shows “why”.
  2. Each ADR is short and has five parts: title, status, context, decision and consequences.
  3. The context and the bad consequences are the most important parts.
  4. Keep the document next to the code and review it with a Pull Request.
  5. Do not delete or edit an old document. Write a new ADR that replaces it.