Engineering Practices

Architecture Decision Records: Stop Losing Your Mind (and Your Decisions)

Architecture Decision Records: Stop Losing Your Mind (and Your Decisions)

Any engineer who has worked on long-lived systems knows the feeling. Late afternoon, deep in a refactor, you hit a piece of code or a configuration or an integration contract that makes no sense. You search the commit history. Nothing useful. You search the chat archives. Nothing. You ask the longest-serving engineer on the team and they look briefly haunted and say “I think Gary did that, but Gary left in 2022.”

Gary is gone. The context is gone. The decision is fossilised in production, completely divorced from the reasoning that created it. And in long-lived systems, in regulated industries, in anything with real integration surface, this is the failure mode that quietly compounds. The half-life of architectural context is shorter than most of us want to admit. Senior engineers move teams. Junior engineers grow into senior ones. The reasoning that shaped a system in 2019 is not necessarily reasoning that anyone alive in the codebase today still holds.

Architecture Decision Records exist to solve exactly this problem. They are not a silver bullet. They are not a replacement for good engineering culture. But used well, they are one of the highest-leverage practices a team can adopt, and they cost almost nothing to start.

What Is an ADR?

An Architecture Decision Record is a short document that captures a single, significant architectural decision along with the context that motivated it and the consequences that followed.

The concept was formalised in Michael Nygard’s 2011 blog post, which remains the foundational reference. The idea is deliberately lightweight. An ADR is not a design document. It is not a spec. It is not a whitepaper. It is a tight, focused answer to one question: why did we do it this way?

That focus is the entire point. The teams that get the most value from ADRs are the ones that resist the urge to expand them into something grander. The ones that don’t, end up with a parallel documentation system that drifts away from reality within a year and gets quietly abandoned within two.

What Goes In One?

Nygard’s original template has five sections, and for most teams that is all you will ever need. The best way to understand them is to walk through one.

Picture a decision that comes up often in long-lived systems with serious integration surface: should a new service consume an upstream feed synchronously, or sit behind a queue and process asynchronously? Both are defensible. The wrong choice is operationally painful for years. This is exactly the kind of decision that deserves an ADR.

You start with a title. Short, present-tense, imperative, searchable. Something like ADR-0014: Consume the upstream availability feed asynchronously via a message queue. That title tells you exactly what is inside before you open it. Compare with Feed integration or Queue stuff, the kind of titles that get written at 4:30pm on a Friday and haunt the repo for years. Pick the first one.

Underneath the title sits the status. One of Proposed, Accepted, Rejected, Deprecated, or Superseded. This single field does an enormous amount of work. It is the difference between someone reading your ADR as historical curiosity and someone reading it as live policy, and that distinction is worth being explicit about.

Then comes the context, which is where most ADRs live or die. This is where you write down what was happening when the decision was made. The upstream feed is published at unpredictable intervals. Latency requirements on the consuming side are measured in seconds, not milliseconds. Tight synchronous coupling to an external dependency would mean a partner’s two-hour outage becomes a two-hour outage for you. Operational ownership for queue infrastructure is already in place. None of these facts will be obvious to a future reader staring at the code. All of them are essential to understanding why the decision made sense at the time. That is the point of context. Not “this is what we built”, but “this is the world we built it in”.

The decision itself is usually the shortest section. Written in present tense, as a clear statement of what is being done. “We will consume the upstream availability feed asynchronously via a managed message queue, with a retention window sufficient to ride out a four-hour upstream outage without data loss.” Not “we are thinking about”, not “we decided last Tuesday”. Direct, declarative, easy to quote in a meeting two years from now when someone proposes something that contradicts it.

Finally, the consequences. What gets easier, what gets harder, what new problems you have just bought. The system gains resilience to upstream outages. It loses the simplicity of a direct call. The team now has a new piece of infrastructure to monitor, a new failure mode (queue backlog) to alert on, and a new latency budget to think about during incidents. Consequences are where honesty matters most. An ADR that lists three benefits and zero costs is not an ADR. It is a sales pitch, and any senior engineer reading it will discount the whole document accordingly.

For teams that want more structure, the MADR (Markdown Architectural Decision Records) format adds sections for decision drivers, considered options with pros and cons, and a confirmation mechanism. MADR is excellent for genuinely contested decisions where the trade-off analysis is the value. For most decisions, it is overkill, and the extra structure becomes friction that discourages writing the ADR at all. Start with Nygard. Reach for MADR only when the decision genuinely warrants it.

One rule sits above all of this: one ADR, one decision. The temptation to bundle related decisions into a single document is strong, especially when they were all made in the same meeting. Resist it. Combined decisions cannot be superseded cleanly. Eighteen months later, when half of the bundled decision is still right and the other half needs reversing, you will be left writing an awkward ADR-0023 that supersedes “the third bullet point of ADR-0014”. That is not a chain anyone wants to maintain. Split them up, even when it feels pedantic. Especially when it feels pedantic.

What a Weak ADR Looks Like, and What a Strong One Looks Like

The five sections are easy to fill in badly. Whether an ADR earns its place or gets quietly ignored usually shows up in how each section is written, and a side-by-side comparison makes the point faster than any amount of guidance.

Here is an ADR that has technically followed the template but delivers almost no value:

# ADR-0007: Use a message queue

## Status
Accepted

## Context
We need to integrate with the upstream availability feed.
The feed is unreliable.

## Decision
We will use a message queue.

## Consequences
The system will be more resilient.

Nothing here is factually wrong. It documents the decision. But everything it says, the code already says. A future engineer staring at the queue infrastructure can already see a queue is being used. What they cannot see is why a queue rather than a direct call, what “unreliable” actually meant, or what alternatives were rejected. This ADR documents the conclusion without preserving any of the reasoning. Within a year, nobody will reference it.

Here is the same decision documented in a way that earns its keep:

# ADR-0014: Consume the upstream availability feed asynchronously via a message queue

## Status
Accepted, 2024-03-12

## Context
The new availability service must consume an upstream feed published by an
external partner. The feed is published at unpredictable intervals, with
historical partner uptime around 99.5% and outages occasionally lasting two
to four hours. Our consuming service has a soft latency target measured in
seconds, not milliseconds. The team already operates managed queue
infrastructure, so adding another queue is incremental rather than new.

## Decision
We will consume the feed asynchronously via an adapter service that publishes
each event onto an internal message queue. Retention will be set to four
hours, sufficient to ride out the longest historical partner outage.

## Considered Alternatives
- Direct synchronous consumption. Rejected: couples our uptime to the partner's.
- Polling at fixed intervals. Rejected: the feed's cadence is too unpredictable
  for polling to be either timely or efficient.
- Partner-pushed webhooks. Rejected: not supported by the partner.

## Consequences
The system gains resilience to upstream outages within the retention window.
It gains an additional piece of infrastructure to monitor and a new failure
mode (queue backlog) to alert on. End-to-end latency now includes queue dwell
time. The four-hour retention is a deliberate choice based on observed
partner outages; if partner reliability changes materially, this decision
should be revisited.

The second ADR is several times longer, and that extra length is doing real work. The title is specific enough that the decision can be guessed before opening the file. The context grounds the decision in concrete operational reality. Considered Alternatives explicitly closes off options that would otherwise be re-debated six months later. Consequences are honest about what the decision costs, not just what it gains. The final sentence about revisiting tells a future reader exactly what should trigger a new ADR. None of these patterns are hard to apply. They mostly require taking the writing seriously, and that is the work most teams skip.

What Qualifies as an Architectural Decision?

Think about the last time your team switched message brokers, or moved from synchronous HTTP calls to an event-driven pattern, or chose to keep everything in a monolith despite vocal pressure to split it up. Those are the decisions that look obvious in the moment and completely mysterious eighteen months later. They are the ones that need ADRs.

The naming convention for your feature flags does not. Neither does your tab-width preference, however deeply held. The useful test is: how hard is this to undo, and how widely will it be felt? The AWS Prescriptive Guidance on ADRs points to decisions that affect structure (patterns, microservices vs. monolith), non-functional requirements (security, scalability, fault tolerance), and anything that is genuinely hard to reverse. If you could rip it out next sprint with minimal drama, it probably does not need an ADR. If undoing it would require a project, a migration plan, and several difficult conversations with people who have opinions, it does.

The MADR project adds another useful prompt: if this decision has been a troublemaker before, somewhere in your system’s history, that is a strong signal it deserves to be written down. Recurring debates are a symptom of decisions that were never properly closed. If your team keeps having the same argument every quarter, the cost of that argument is already higher than the cost of writing it down.

Where to Store Them

The answer is more settled than the debate suggests: store ADRs in the same Git repository as the code they govern.

Decisions are coupled to the system. When the code moves, the reasoning should move with it. Storing ADRs in the repository means they are versioned alongside the codebase, discoverable in pull requests, linkable from commit messages, and subject to the same review workflow as everything else. The MADR convention is to put them under docs/decisions/ with filenames like 0001-use-postgresql-as-primary-datastore.md. Four-digit numbering keeps them sortable. For larger systems, subdirectories organised by architectural concern (backend, frontend, infrastructure) work well, with numbering local to each subdirectory.

Wikis and Confluence are a reasonable second choice when non-technical stakeholders need regular access, but the risk is drift. The code evolves. The Confluence page does not. Six months later you have documentation that looks authoritative but actively misleads people. A beautifully formatted page titled “Our Messaging Architecture” that describes a system decommissioned in Q3 is not documentation. It is a trap. Git, with its pull request friction and version history, works against that tendency.

Should the Code Link Back to the ADR?

A question that comes up often, and the answer is more nuanced than a yes or no.

The case for linking is real. When a future engineer is staring at code that looks wrong or surprising, a comment like // See ADR-0014 for rationale collapses what could be a thirty-minute archaeology session into a thirty-second click. This is genuinely valuable at the seams: integration boundaries, retry logic, timeout values, places where the code violates an obvious principle for a non-obvious reason. Anywhere the “why” is surprising, a pointer to the ADR is worth the line.

The case against blanket linking is also real. If every architectural decision generates a comment, the codebase ends up littered with // See ADR-XXXX notes that nobody reads after the third one. Comments rot. Links break when ADRs get renumbered or restructured. And the link itself can quietly become a substitute for code that should explain itself: “this looks weird, but ADR-0014 says it’s fine” is not the same thing as making the code less weird.

The right rule is selective. Link from code to ADR when the code embodies a counterintuitive choice, when it implements a constraint that comes from the ADR rather than from the immediate problem (a retry policy that exists because of an upstream contract rather than local logic), or when the code sits at a boundary where the ADR’s reasoning is most likely to be questioned by a future reader. Skip the link when the code is straightforward, when the decision is reflected uniformly across the codebase (a chosen ORM, a chosen logging library), or when the link would be ceremonial rather than useful.

A reverse pattern is often higher value than people realise: linking from the ADR back to the code. A short “Implementation” or “References” section in the ADR pointing at specific files, modules, or commits gives a future reader a starting point for seeing how the decision actually landed. This direction does not rot the codebase, and it stays useful even when ADRs get superseded, because the new ADR can point at the new code while the old chain remains intact for anyone tracing history.

Lifecycle and Review

An ADR is not a static document. It moves through states, and treating those states seriously is what separates a useful decision log from a graveyard of markdown files everyone has quietly agreed to ignore.

A new ADR starts as Proposed, drafted by an owner and circulated for review. The review itself works best when it is short and structured. The AWS Architecture Blog recommends 30–45 minute meetings with the first 10–15 minutes reserved for participants to actually read the document, which is more radical in practice than it sounds. The goal of the review is to surface objections, stress-test the trade-offs, and reach genuine consensus rather than the polite version where everyone nods and three people privately disagree. Sign-off from architects, engineers, product, and operations is not bureaucracy. It is what makes the accepted document credible.

Once the team accepts the ADR, it becomes Accepted and immutable. Do not edit it to reflect new information. Create a new one. This append-only approach, which Microsoft’s Well-Architected Framework describes directly as “the ADR serves as an append-only log,” is the discipline that makes the whole practice work. The history of the thinking is as valuable as the current state of the decisions.

Decisions that get turned down go to Rejected, and this is the status teams skip most often, which is a mistake. A rejected option documented in one ADR prevents the same option being proposed, debated, and rejected again six months later by a different set of people who were not in the room. Nothing deflates a heated architecture debate quite like “we covered this in ADR-0012.” Gary may be gone, but ADR-0012 is not.

Deprecated is for decisions that are no longer relevant but have no direct replacement. Superseded is for decisions replaced by newer ones, with the old ADR linking forward to the new and the new linking back. The chain stays intact.

A separate, looser review cadence is worth maintaining for accepted ADRs. Many teams schedule a check one month after acceptance, comparing what was expected against what actually happened. This is not about changing the ADR (immutable, remember) but about learning what to do differently next time. A periodic sweep through older ADRs is also worthwhile. Technology moves, priorities shift, and a decision that was excellent three years ago may now be quietly pointing in the wrong direction. The right response is a superseding ADR, not a quiet edit.

The Benefits, Honestly

The pitch for ADRs sometimes oversells what they do. Here is the honest case, with the marketing stripped out.

The first benefit is institutional memory, and it is the one teams underestimate most. Engineers move. Teams reorganise. Senior people leave. In a long-lived system, the half-life of architectural context is shockingly short, and once it has gone, you cannot get it back. A well-maintained ADR log is the closest thing you have to insurance against that loss. When a new starter asks “why are we doing it this way”, the answer is a document, not a Slack thread that spirals into a two-hour debate about a decision someone made years ago.

The second benefit is sharper thinking at the point of decision. This one is harder to quantify but easy to recognise once you have lived through it. Writing “we will use a monolith because the team has three engineers and a microservices architecture would triple our operational complexity” is a sentence that is genuinely difficult to write unless you have thought it through. Martin Fowler makes this point well: the act of writing forces disagreements to surface. Decisions made verbally in a meeting and never written down tend to be the ones where everyone thinks they agreed and nobody actually did. The ADR is the test.

The third benefit is accountability without blame. When a decision turns out to have been wrong, an ADR does not point the finger at anyone. It shows the context that made the decision reasonable at the time. You are not proving someone made a bad call; you are showing that the situation changed. Those are different things, and treating them as different is what separates teams that learn from teams that play politics. There is published research backing this up too: a 2024 action research study via ECSA introduced ADRs to a microservices team and reported clear improvements in documentation culture, knowledge transfer, and decision prioritisation within three months. Anecdotally satisfying, empirically supported.

What ADRs do not do is fix a broken decision-making culture. They are a tool, not a strategy. If the underlying problem is that decisions get made without the right people in the room, or that decisions get made and then quietly ignored, an ADR will document the dysfunction without curing it. The teams that get the most out of ADRs are the ones that already take decisions seriously. For everyone else, ADRs are part of a larger conversation about how the team operates.

Where ADR Adoption Goes Wrong

ADRs are simple. ADR adoption is not. The pattern across the industry is consistent: the practice gets introduced, fails to stick, and the team quietly gives up. Most posts on this topic skip past the failure modes, but they are worth naming because anyone introducing ADRs needs to know what to avoid.

The first failure mode is ADRs as theatre. The template gets created. A folder gets added to the repo. A handful of ADRs get written for decisions that have already been made and are not contentious. Nobody reads them. Nobody references them. The folder sits there as a polite gesture toward documentation discipline, and within a year it is forgotten. The fix is structural: connect ADRs to the work, make them part of the design conversation rather than a record of conversations that already happened. An ADR written before a decision is made is genuinely useful. One written after the fact, just to fill the folder, rarely survives contact with reality.

The second is ADRs as bureaucracy. The opposite extreme. The process around ADRs becomes heavier than the decisions they document. Mandatory templates, mandatory reviews, mandatory sign-offs from people three layers removed from the work. The friction of writing one becomes high enough that engineers start avoiding it, which means the decisions that most need documenting are the ones that don’t get written down. ADRs should make decisions easier to land, not harder. If a process is producing the opposite effect, the process is wrong.

The third is ADRs that ossify. A decision was right when it was made, but the world has moved on. Years later, the original ADR is still treated as authoritative, the system has been quietly working around it, and the superseding ADR never gets written. The original document hardens into a load-bearing piece of inertia. The fix is cultural: superseding an ADR should be treated as a routine event, not a referendum on the original author. If writing a new ADR feels like a confrontation, the team has a different problem and the ADR is not the solution.

The fourth, and most subtle, is ADRs that document the wrong layer. Teams write ADRs for technology choices (“we will use Postgres”) but skip the architectural patterns that actually shape the system: how service boundaries get drawn, how eventual consistency is handled, how data ownership is decided. The technology choices age out within years. The patterns persist for the lifetime of the system. An ADR log that captures only the easy decisions ends up documenting the least important ones.

None of these failure modes are reasons to skip ADRs. They are reasons to be deliberate about how the practice is introduced, and to keep an eye on whether it is actually doing what it should.

A Note on ISO 9001 and 27001

ADRs are an engineering practice, not a compliance tool. But for organisations operating under ISO 9001 or 27001, a well-maintained ADR log does a surprising amount of compliance work as a side effect. They document significant decisions, live under version control, have a defined review process, and follow an append-only lifecycle that maps cleanly onto continuous improvement evidence. They do not replace the formal documents either standard requires, but they complement them and reduce the audit burden meaningfully. The compliance benefit is a tailwind rather than a reason to adopt the practice, but it is real, and worth a longer treatment than fits in this post. I will return to it separately.

Tooling Worth Knowing About

The baseline toolchain is a text editor and Git, which is all most teams actually need. But if your ADR collection grows large enough that discoverability becomes a problem:

  • adr-tools — A command-line tool for creating and linking ADRs in Markdown format. Solid, simple, gets out of your way.
  • Log4Brains — Generates a browsable static site from your ADR directory. Useful when you want stakeholders to read decisions without touching Git.
  • ADR Manager — A web app that connects to GitHub and provides a CRUD interface for MADR-formatted records.
  • Backstage ADR plugin — For teams already running Spotify’s Backstage developer portal, this surfaces ADRs alongside service documentation.

Getting Started

The hardest part is not writing the first ADR. It is building the habit.

The lowest-friction approach: pick the next significant decision your team is about to make, write an ADR for it before the decision is finalised, and send it to the team as a proposed document. Run the review. Accept it. Store it in the repo. Repeat.

A note from the joelparkerhenderson architecture-decision-record repository, which is the most comprehensive community reference on the subject: some teams find the word “decisions” works better than “ADRs” as the directory name. When the folder is called decisions/, people start putting more things in it. Vendor decisions. Timing decisions. Process decisions. That is not a bad thing.

If you are inheriting a system without ADRs, write retroactive ones for the decisions you can still reconstruct. Sit down with the people who were there, work through the major architectural choices that shape the system today, and document them in present tense as though the decisions were being made now. The act of writing them is genuinely useful, even years after the fact. Assumptions get surfaced. Decisions that were never really made, but quietly drifted into, get identified. Pieces of architecture nobody can explain anymore get flagged for proper investigation. Better late than never. Definitely better than Gary.

Start small. Write the ADR for the decision you are making this week, while the context is still in everyone’s heads and the trade-offs are still fresh.

In two years, someone will open that file at 11pm trying to understand why the system works the way it does. They will read the context, the reasoning, the alternatives that were rejected and why. They will not need to track anyone down on LinkedIn. They will not have to convene a meeting to reconstruct what was already known.

They will just know. And quietly, from wherever you are by then, you will have saved them.

Further Reading

The four resources worth returning to: