Architecture Decision Records: A Working Guide
Use concise architecture decision records to capture context, options, consequences, and revisit triggers so teams can understand design choices later.
Architecture Decision Records (ADRs) capture the context, options, consequences, and revisit triggers behind consequential system choices. Using an order-event outbox as an example, this guide shows how to record trade-offs, manage proposal and acceptance status, connect decisions to implementation evidence, and review assumptions over time. Use the included template and checklists to make design reasoning easier to find when a system or its constraints change.
Architecture Decision Records: A Working Guide
Introduction
Suppose checkout writes an order to a database and then publishes an event to a message broker. A crash between those operations can leave the order saved with no event sent. An Architecture Decision Record (ADR) can capture why the team chose an outbox, which alternatives it rejected, and what operating costs came with that choice.
This guide covers when to write an ADR, what to include, and how to revisit one when its assumptions change. The example uses order events, but the same record format works for decisions about service boundaries, data ownership, or consistency.
When to Use / When Not to Use
Write an ADR when a decision affects multiple teams or system boundaries, introduces a lasting constraint, or is expensive to reverse. Examples include data ownership, eventual consistency, a message broker, or splitting a deployable service.
Skip ADRs for local, reversible choices covered by team conventions. Record small choices in a pull request or issue. An ADR should explain why an option was selected and what would justify changing it.
Core Concepts
Keep an ADR short enough to read during code review. Include:
- Title and status: Proposed, accepted, superseded, or deprecated.
- Context: The problem, constraints, and quality attributes that matter.
- Decision: The option the team chose, stated plainly.
- Consequences: Benefits, costs, risks, and operational obligations.
- Alternatives: Serious options considered and why they did not fit.
- Revisit trigger: Evidence or a changed condition that would reopen the decision.
Treat ADRs as append-only: if circumstances change, create a new record that supersedes the old one and link the pair. The history stays readable without implying that old decisions remain correct forever.
flowchart LR
Question[Architecture question] --> Context[Capture constraints and goals]
Context --> Options[Compare credible options]
Options --> Record[Write decision and consequences]
Record --> Implement[Link implementation and evidence]
Implement --> Review[Check revisit trigger later]
Review -->|New conditions| Question
The ADR Lifecycle
An ADR remains useful after it is merged because its status and links show how the decision changed over time. A small status vocabulary is usually enough:
- Proposed: The author has stated the problem, constraints, and options. Reviewers can challenge assumptions before the choice is committed.
- Accepted: The accountable team has made the decision. Link the record from the implementation pull request or the relevant module documentation.
- Superseded: A later ADR changes the decision. Link both records so readers can follow why the direction changed.
- Deprecated: The decision no longer applies, and no replacement is needed. State why it ended and what replaced its constraints, if anything.
Review an accepted ADR when a stated trigger occurs, its assumptions stop holding, or a planned review date arrives. A lightweight review should check whether the implementation still follows the decision, whether its costs are visible in operational data, and whether the record needs a successor. Do not reopen a decision just because a different option is fashionable; compare it against the original constraints and current evidence.
Before accepting a consequential ADR, reviewers can use this short checklist:
- Is the problem and its boundary clear enough that another team can recognize when the decision applies?
- Are the quality attributes measurable through scenarios or operating targets?
- Were credible alternatives compared against the same constraints?
- Are consequences, owners, and operational obligations explicit?
- Does the record name evidence or a condition that would justify revisiting it?
A Decision Template
Store records in a predictable location such as docs/architecture/decisions/, with stable identifiers such as 0007-use-outbox-for-order-events.md.
ADR 0007: Publish order events through an outbox
Status: Accepted
Date: 2026-10-03
## Context
Order writes must be durable before downstream consumers are notified.
The database and broker do not share a transaction.
## Decision
Write an outbox row in the order transaction. A worker publishes rows
and marks them sent after broker acknowledgement.
## Consequences
- A committed order always has a corresponding publishable event.
- Consumers must handle duplicate delivery; the worker can retry.
- The team must monitor outbox age and dead-letter repeated failures.
## Alternatives
- Dual write: simpler, but a crash can persist only one side.
- Synchronous broker publish: couples checkout availability to the broker.
## Revisit when
The event volume or delivery latency target makes polling insufficient.
The outbox pattern guide explains the pattern. The ADR records the local choice and operating consequences. See quality attributes and architecture trade-offs for measuring goals before comparing options.
Trade-off Analysis
| Practice | Advantage | Cost or risk |
|---|---|---|
| One decision per record | Easy to discuss, supersede, and search | Related decisions may need cross-links |
| Short structured template | Consistent scanning and review | A rigid template can invite empty sections |
| Store with source code | Version history follows implementation | Requires repository discoverability and access |
| Link ADRs from pull requests | Connects reason to code and evidence | Links can rot if they target temporary systems |
| Record rejected options | Preserves the useful comparison | Requires concise writing to avoid a meeting transcript |
Keep records findable and current in status. A template should prompt useful thinking, not paperwork.
Production Failure Scenarios
ADRs become stale but appear authoritative. Readers follow an accepted record after its assumptions have changed. Add status, date, owner, and revisit triggers. Mark superseded records clearly and link to the replacing decision.
A decision record promises behavior the system does not implement. Link the ADR from the relevant pull request or module documentation, then revise or supersede it when implementation changes. A record shows intent, not runtime behavior.
Sensitive details end up in a broadly readable repository. Avoid credentials, personal data, exploit details, and restricted vendor information. Describe the decision at an appropriate level and point to an access-controlled security record when needed.
Observability Checklist
ADRs are not runtime telemetry, but they can state what the team must observe:
- Identify metrics that test its main assumption, such as queue lag or p95 latency.
- Name the dashboard, alert, or runbook that supports the operational consequence.
- Record an owner for follow-up when the decision introduces ongoing work.
- Set a review date or measurable revisit threshold for uncertain decisions.
- Compare operational evidence with the quality scenario that motivated the choice.
The metrics and monitoring guide covers choosing measures and alerts for system behavior.
Security and Compliance Notes
ADRs can support auditability by recording when a decision was made and how security or data requirements shaped it. They do not replace risk assessments, threat models, privacy impact assessments, or compliance evidence. Keep restricted details in approved systems and record residency, retention, access, and deletion constraints when relevant.
Common Pitfalls / Anti-Patterns
- Writing a chronology of meetings instead of a decision and its reasons.
- Recording only the chosen option while hiding credible alternatives and costs.
- Using vague context such as “we need scale” without scenarios or constraints.
- Treating accepted as permanent, or editing history when the decision changes.
- Creating ADRs for trivial choices until teams stop reading them.
- Leaving records detached from pull requests, modules, and operational evidence.
Quick Recap Checklist
- Write one ADR for one consequential decision.
- State the context and constraints before stating the choice.
- Include real alternatives, consequences, risks, and operational obligations.
- Give the record a clear status and stable identifier.
- Link implementation and evidence, then supersede rather than erase changed decisions.
Interview Questions
Write one when the decision has meaningful impact across boundaries or teams, is costly to reverse, creates an ongoing operational obligation, or depends on assumptions a future team may need to revisit.
Yes, when they were credible choices. A short note about why an option did not fit helps future readers avoid repeating the comparison and shows which constraints drove the decision.
Create a new record that describes the changed context and replacement decision. Mark the old ADR as superseded and link it to the new one so the history remains clear.
An overview describes the system's current shape. An ADR explains one consequential choice, the constraints behind it, and the consequences the team accepted. They complement each other and should link together when useful.
The team accountable for the affected system should own the decision. A named author can maintain the document, but acceptance should come from the people responsible for its consequences, including operations or security when their work changes.
Include the problem boundary, constraints, relevant quality attributes, and assumptions that distinguish the options. Keep meeting chronology and background that does not affect the choice out of the record.
Keep the ADR in proposed status and make the unresolved question explicit. Record options and decision criteria without presenting a preferred direction as settled; update the status once the accountable team decides.
Use the decision's revisit trigger or review date rather than scheduling every record on the same cadence. Review sooner when assumptions change, incidents expose a consequence, or operational measures miss the target that motivated the choice.
Include details only when they explain or constrain the decision. Put evolving procedures and code-level instructions in implementation documentation, then link to them so the ADR can stay short and durable.
Supersede it when another decision replaces it. Deprecate it when the decision no longer applies and there is no replacement choice to document, such as when the system or affected capability is retired.
Further Reading
- Architecture Decision Records — formats, examples, and community resources.
- Documenting Architecture Decisions — the lightweight record approach commonly associated with ADRs.
- ADR examples and templates — an open collection of formats and examples.
- Continue with the architecture styles and patterns guide and the Software Architecture Patterns Roadmap.
Conclusion
An ADR gives future teams the evidence behind a choice, including the constraints that may no longer apply. When those conditions change, a new record can explain the next decision while leaving the old reasoning available for context.
Category
Related Posts
Architecture Styles and Patterns: A Practical Guide
Compare layered, hexagonal, event-driven, and service architectures using boundaries, deployment needs, failure modes, and a concrete selection method.
Architecture Fitness Functions as Executable Guardrails
Turn architecture principles into executable fitness functions that check dependency rules, performance limits, and deployment constraints as systems evolve.
Behavioral Patterns: Organize Collaboration
Compare all eleven GoF behavioral patterns by the change they isolate, the coupling they reduce, and the runtime costs they add to a system in real code.