Architecture Decision Records That Engineers Will Actually Read

7 min read

Most software systems embody decisions that nobody can fully explain.

A database was chosen three years ago. An internal protocol includes an unusual field. Two services share a deployment pipeline despite being supposedly independent. A team knows that a particular library must not be upgraded—but nobody remembers why.

The code shows what a system does. It rarely preserves the full reason it ended up that way.

Architecture Decision Records (ADRs) provide a lightweight way to preserve that reasoning. An ADR is a short document describing a consequential decision, its context, and its implications.

The idea is simple. Making it useful takes discipline.

Record decisions, not meeting history

An ADR should not be a transcript of every conversation. It should explain the decision a future engineer needs to understand.

A practical record answers five questions:

  1. What problem required a decision?
  2. Which constraints shaped the available choices?
  3. What did the team decide?
  4. Which credible alternatives were considered?
  5. What consequences does the team accept?

That is enough for most decisions.

Details such as benchmark results, diagrams, incident reports, or proof-of-concept code can be linked rather than copied into the document.

The goal is not to reproduce the past. It is to make the decision understandable.

Capture context before the solution

Weak ADRs often begin with a technology:

> We will use Kafka.

That statement records an outcome but provides little architectural value. It does not explain which problem Kafka solves or how to evaluate the decision later.

A stronger record begins with context:

> The ingestion platform must accept bursts that exceed downstream processing capacity, preserve event order within each device, and allow consumers to recover independently. Direct synchronous delivery currently couples ingestion availability to three downstream systems.

Now the decision can be evaluated against explicit needs.

Context should include the constraints that genuinely influenced the choice:

  • Expected throughput and latency
  • Ordering requirements
  • Data-loss tolerance
  • Regulatory obligations
  • Supported deployment environment
  • Operational capability
  • Existing technology
  • Team ownership
  • Delivery schedule
  • Cost boundaries
  • Compatibility requirements

Constraints prevent future readers from judging an old decision by circumstances that did not exist at the time.

Keep one decision per record

An ADR titled “New platform architecture” is usually too broad.

It may contain decisions about deployment, messaging, data ownership, authentication, monitoring, and service boundaries. When one of those decisions changes, the document becomes difficult to update without implying that everything changed.

Smaller records are easier to review and supersede:

  • Use asynchronous ingestion between devices and processing
  • Partition events by device identifier
  • Store raw events for seven days
  • Use PostgreSQL as the source of truth for configuration
  • Authenticate service traffic using workload identities
  • Adopt an expand-and-contract process for schema changes

These decisions may be related, and the ADRs can link to one another. Keeping them separate clarifies their lifecycle.

Include real alternatives

An ADR is not persuasive when the chosen option is compared with obviously unsuitable alternatives.

If a team selects PostgreSQL, listing “store everything in text files” as the rejected alternative does not show meaningful evaluation.

Document the strongest credible options. For each, explain the decisive trade-off rather than producing a huge feature matrix.

For example:

Option A: synchronous service calls

Simple request flow and immediate responses, but ingestion availability remains coupled to downstream latency and outages.

Option B: managed message broker

Separates ingestion from processing and absorbs temporary bursts, but introduces eventual consistency and requires consumer-lag monitoring.

Option C: database-backed work queue

Uses existing operational knowledge and supports transactional insertion, but offers weaker independent scaling and routing than the broker.

The chosen option becomes more trustworthy when readers can see why reasonable alternatives were rejected.

State consequences honestly

Every significant architectural decision has drawbacks.

A record that lists only benefits is either incomplete or promotional.

Choosing asynchronous messaging may improve decoupling while introducing duplicate delivery, delayed consistency, message-versioning requirements, and more difficult end-to-end debugging.

Choosing microservices may support independent deployment while increasing operational overhead, network failure modes, and cross-service data challenges.

Choosing Rust for a component may provide memory safety and predictable performance while narrowing the internal hiring pool or increasing initial development time.

Consequences should include new responsibilities:

  • What must now be monitored?
  • Which failure modes have been introduced?
  • What knowledge will teams need?
  • Which migration work is required?
  • What becomes harder to change?
  • Which assumptions must remain true?

This section often provides more long-term value than the decision statement itself.

Record evidence where it matters

Some decisions are primarily based on reasoning. Others depend on measurable claims.

If a technology is chosen for performance, include the relevant workload and benchmark results. If a design is selected to improve recovery, record the expected recovery objective and how it was tested. If cost is decisive, include the estimated scale rather than simply saying one option is cheaper.

Evidence should be proportional to the decision.

A decision about an internal naming convention does not require a week-long proof of concept. A decision about a protocol expected to support millions of devices deserves stronger validation.

Useful evidence might include:

  • A small benchmark
  • A failure-mode analysis
  • A compatibility experiment
  • An operational cost estimate
  • A security review
  • A deployment rehearsal
  • Production measurements
  • A prototype

The ADR should capture the conclusion and link to the detailed evidence.

Treat decisions as immutable history

When circumstances change, avoid rewriting an accepted ADR as if the original decision never happened.

Instead, create a new record that supersedes it.

For example:

  • ADR-014 selects REST for an internal interface.
  • Two years later, ADR-041 adopts gRPC for new high-throughput calls.
  • ADR-041 links to ADR-014 and explains what changed.

The earlier record remains valuable because it explains the system’s history and the assumptions that once applied.

Martin Fowler’s description of ADRs similarly recommends keeping accepted records intact and linking them to later decisions that replace them. Martin Fowler

Useful ADR statuses include:

  • Proposed
  • Accepted
  • Rejected
  • Deprecated
  • Superseded

Avoid elaborate workflows unless the organization truly needs them.

Keep ADRs close to the work

For software architecture, storing ADRs in the repository often works well. They can be reviewed with the related code, versioned, searched, and updated through the team’s normal workflow.

A simple structure is sufficient:

1docs/ 2 decisions/ 3 0001-use-postgresql-for-device-configuration.md 4 0002-publish-domain-events-through-outbox.md 5 0003-version-external-protobuf-contracts.md

Not every decision belongs in a source repository. Cross-product or organization-wide choices may need a shared documentation system. The principle is proximity: people affected by a decision should be able to find it where they work.

An ADR nobody can find isn’t documentation—it’s storage.

Use ADRs to improve the decision itself

Writing an ADR is valuable even before it’s accepted.

Writing forces vague arguments to become explicit. “This will scale better” becomes “this removes a single-writer bottleneck at the cost of cross-partition queries.” Disagreements are easier to resolve when assumptions and consequences are visible.

A short written proposal also gives quieter team members a fairer opportunity to contribute than a fast-moving architecture meeting.

The ADR is therefore both a historical record and a thinking tool.

A practical template

A useful template can remain short:

1# ADR-012: Use an outbox for reliable event publication 2 3## Status 4 5Proposed 6 7## Context 8 9What problem are we solving? Which constraints and forces matter? 10 11## Decision 12 13What are we choosing? 14 15## Alternatives 16 17Which credible options did we consider, and why were they not selected? 18 19## Consequences 20 21What becomes easier, harder, or newly necessary? 22 23## Evidence 24 25Which measurements, experiments, or references support the decision?

Add sections only when they repeatedly provide value.

Documentation should reduce future confusion

ADRs should not become an approval ceremony for every code change. Reserve them for decisions that are expensive to reverse, affect several teams or components, introduce meaningful operational consequences, or establish a pattern others will follow.

A healthy ADR practice produces short documents, clear trade-offs, and links between evolving decisions.

Its success is not measured by the number of records written.

It is measured six months later, when an engineer asks, “Why is the system designed this way?”—and receives a useful answer.