Every long-lived software system contains decisions that nobody can explain. Why does the order service use a different database than the rest? Why was this message broker chosen over the one the company uses elsewhere? Why is there a seemingly pointless translation layer between two modules? The people who made those choices have moved on, the discussion lived in a chat thread that has since been archived, and the code shows what was built but not why.
New team members then face an unpleasant choice. They can leave the mystery alone, accepting constraints they do not understand, or they can change it and risk breaking something that was there for a good reason. Often they reverse a decision without knowing it was carefully weighed, and the team rediscovers the original problem months later. Michael Nygard described this problem in a well-known 2011 article, and proposed a simple remedy: a short document for each architecturally significant decision, kept with the code. He called it an Architecture Decision Record, or ADR.
This article explains what ADRs are, what makes a decision worth recording, how to structure and store them, how to build a lightweight review process around them, and how to avoid common failure modes. It ends with a worked example of an ADR for a real-world choice.
What an Architecture Decision Record Is
An ADR is a short text document describing one significant decision, the situation that prompted it, the options considered, and the expected consequences. It is deliberately small, typically one or two pages, and it is written at the time the decision is made, while the reasoning is fresh.
An ADR is not a design document, which describes an entire system or feature in depth. It is not meeting minutes, which record everything said. And it is not a specification of how to implement something. It answers one narrow question: what did we decide, and why?
ADRs are also immutable in a particular sense. Once a decision is accepted, the record is not rewritten when the decision later changes. Instead, a new ADR is created that supersedes the old one, and the old one is marked accordingly. The sequence of records therefore forms a historical log of how the architecture evolved, including the paths not taken and the reasons circumstances changed.
Which Decisions Deserve a Record
Not every choice warrants an ADR. Recording trivial decisions creates noise and discourages the practice. A useful guide is the idea of an architecturally significant decision: one that has a wide impact, is costly to reverse, or affects the quality attributes of the system such as performance, security, scalability, or maintainability.
Typical candidates include selecting a programming language or framework for a new service, choosing a database or messaging technology, deciding between a monolith and microservices for a component, adopting an authentication approach, defining an API style or versioning policy, choosing a cloud provider or deployment model, establishing a data retention or multi-tenancy strategy, and making a significant build-versus-buy choice. Decisions that introduce a new dependency with long-term implications, or that deliberately break a previous convention, are also good candidates.
A practical test is to ask whether a new engineer, one year from now, would be likely to wonder why this was done that way. If the answer is yes, write it down. Another test is whether the decision would be expensive or embarrassing to reverse. When in doubt, favor a short record over none.
The Anatomy of a Good ADR
Nygard's original template is simple and has become the most widely used. It consists of a title, a status, the context, the decision, and the consequences. Several extensions have emerged.
The title is short and numbered, for example "ADR 0012: Use PostgreSQL for the billing service." The status indicates where the decision stands: proposed, accepted, rejected, deprecated, or superseded by a later record. The context describes the forces at play, which include the problem, constraints, requirements, and relevant facts, written neutrally. It explains why a decision is needed at all. The decision states what was chosen, in active voice and clear terms, such as "We will use PostgreSQL." The consequences describe what becomes easier, what becomes harder, and what new risks or obligations follow, including the negative ones. Honest consequences are the most valuable part of the record, because every decision is a trade-off.
Many teams use a richer format known as MADR, short for Markdown Any Decision Records. It adds explicit sections for the decision drivers, the considered options with their pros and cons, and the outcome with justification. Others use Y-statements, a compact one-sentence form: in the context of a situation, facing a concern, we decided for an option, to achieve a quality, accepting a downside. The format matters less than the discipline of capturing alternatives and trade-offs. Documenting only the winning option misses the point, since the rejected alternatives are what prevent future teams from re-proposing them unknowingly.
Where to Store ADRs
The strongest convention is to keep ADRs in the same repository as the code they concern, usually in a directory such as docs/adr or doc/architecture/decisions, written in plain Markdown files with numbered names. This approach has important advantages. The records are versioned along with the code, so one can see the decisions that applied at any point in history. They are reviewed through the same pull request process as code changes. They are easy to find, since they sit next to the thing they describe. And they require no additional tooling or licenses.
For decisions that span many repositories or the whole organization, such as the company-wide choice of a cloud provider or identity platform, a dedicated architecture repository or an internal documentation portal may be better. What matters is that records are discoverable, searchable, and linked from places developers already look, such as the README or the contributor guide.
Several tools support the practice. adr-tools provides command-line helpers for creating and linking records. Log4brains generates a searchable static website from the files and integrates with continuous integration. Backstage and other developer portals can surface ADRs alongside service catalog entries. These tools are conveniences. A folder of Markdown files and a naming convention are enough to begin.
The Process: Lightweight Governance
ADRs are most effective when combined with a simple decision process. Traditional architecture governance often relied on review boards that met monthly, produced heavyweight documents, and slowed teams down. Modern practice favors a model of decentralized decision-making supported by transparency, often called the advice process.
In this model, anyone can make an architectural decision, provided they first seek advice from those affected and from people with relevant expertise. The decision-maker writes a proposed ADR, shares it for comment, typically through a pull request, and then decides after considering the feedback. The decision remains with the person or team accountable for the outcome, and the record shows who was consulted and what was learned. This approach preserves speed and autonomy while ensuring that important choices are visible and challenged.
Some organizations add guardrails. A small architecture group or community of practice may review ADRs that cross team boundaries or affect shared platforms. Decisions that conflict with agreed principles or standards, such as a technology radar that marks certain tools as hold, require an explicit justification. These guardrails guide rather than block, and they keep review light by limiting it to decisions with broad impact.
A pull request workflow works well. The author opens a pull request with the new file in the status proposed. Reviewers comment, ask about alternatives, and suggest additional consequences. A time limit, such as one week, avoids endless debate. When the decision is made, the status changes to accepted, and the merged record becomes the official log. If the proposal is rejected, it is still merged with the status rejected, so that the reasoning is not lost.
Connecting ADRs to Architectural Principles and Fitness Functions
ADRs become more powerful when linked to the broader architecture. Teams often define a small set of guiding principles, such as prefer managed services, design for failure, or favor boring technology. Each ADR can reference the principles it supports or the reasons it makes an exception, which keeps the principles alive and honest.
The concept of evolutionary architecture, described by Neal Ford, Rebecca Parsons, and Patrick Kua, adds a further link: architectural fitness functions. A fitness function is an automated check that verifies a characteristic of the architecture, such as a rule that the domain layer must not depend on the persistence layer, or that response time stays under a threshold. When an ADR makes a decision with a testable consequence, the team can encode that consequence as an automated test, using tools like ArchUnit, so that the decision is enforced continuously rather than relying on memory. The ADR explains why, and the fitness function ensures compliance.
Common Pitfalls
ADR practices often fail in predictable ways. The first is writing them after the fact, from memory, which produces rationalizations instead of real reasoning. The best moment is during the decision. The second is making them too long. A record that takes a day to write will not be written. Aim for a page. The third is recording only the decision with no alternatives or consequences, which makes the record nearly useless. The fourth is treating them as bureaucracy, requiring an ADR for every minor change, which causes resentment and perfunctory content. The fifth is letting them go stale: records that are never linked or visible are forgotten, and superseded decisions that are not marked as such mislead readers. The sixth is hiding disagreement. If the team was divided, say so, and capture the dissenting view; it will be valuable if circumstances change.
A subtler pitfall is using ADRs to defend decisions rather than to understand them. A record that reads like advertising for the chosen option, with the alternatives dismissed unfairly, has no value as a learning tool. Aim for neutral, honest language, and include the cons of the chosen solution.
Benefits for Onboarding, Audits, and Culture
The benefits extend beyond avoiding repeated debates. New engineers can read the ADR log in an afternoon and gain a map of the system's history and rationale, which accelerates onboarding. When leadership or auditors ask why a particular technology or control was chosen, the records provide a defensible answer. When a decision is revisited because the context has changed, such as a vendor price increase, the team can see which assumptions no longer hold and revise the decision on evidence.
The practice also shapes culture. Writing forces clarity, since it is hard to justify a choice you cannot articulate. Sharing proposals invites input from people who might otherwise be overlooked, including junior engineers and teams in other time zones. And documenting trade-offs normalizes the idea that architecture involves compromise, not perfection, which makes teams more comfortable with change.
A Worked Example: Choosing a Message Broker
Imagine a team building an order-processing platform. They need to decouple the checkout service from fulfilment and notification services. One engineer proposes using a message broker, and the team must choose between running Apache Kafka, using RabbitMQ, or adopting the managed queue service of their cloud provider. The engineer drafts an ADR in the repository as file 0014 and opens a pull request.
The title reads "ADR 0014: Use the managed cloud queue service for order events." The status is proposed. The context section explains that checkout traffic averages 50 orders per minute with peaks of 600 during promotions, that fulfilment and notifications must be decoupled so that an outage in one does not block purchases, that the team has five engineers and no dedicated operations staff, and that events must be delivered at least once and processed in order per customer. It also notes that the company has no existing Kafka expertise and that analytics teams may want a replayable event history in the future.
The considered options are listed with honest pros and cons. Kafka offers high throughput, long retention, and replay, but demands significant operational effort or a costly managed offering, and the team's volume does not require its scale. RabbitMQ is flexible and well understood, but the team would need to operate clusters and handle upgrades and failover. The managed queue service requires no operations work, scales automatically, integrates with existing identity and monitoring, and is inexpensive at the expected volume, but it offers limited retention and replay, and ties the team more closely to the provider.
The decision reads: "We will use the managed queue service with FIFO queues keyed by customer identifier for order events." The consequences section lists positive effects first: no infrastructure to operate, fast delivery, built-in dead-letter queues, and a predictable cost. It then lists the negatives and risks: no long-term event replay, so analytics will need a separate export of order data; potential vendor lock-in, mitigated by wrapping the queue behind a small internal interface; and throughput limits per FIFO queue, which the team will monitor. It adds a trigger for reconsidering: if sustained volume exceeds 5,000 events per minute or if replay becomes a firm requirement, the team will revisit this decision and evaluate Kafka.
During review, an engineer from the data team comments that analytics will need event history sooner than expected. The author adds the consequence that a CDC export of the orders table will feed the data platform, and links to a follow-up ADR on that topic. A second reviewer asks about message ordering, prompting a clarification in the context. After five days, the author changes the status to accepted and merges the pull request. The team also adds an architectural test verifying that services access the queue only through the internal interface, linking it to the ADR by name.
Fourteen months later, the company launches in a new market, and event volume reaches the stated threshold. A new engineer reads the ADR log, sees the revisit trigger, and proposes ADR 0031, which supersedes 0014 and adopts Kafka for order events. Because the original reasoning was written down, the new decision is quick and informed. The old record is marked superseded, with a link to its successor, and the history remains intact.
Conclusion
Architecture Decision Records are one of the most cost-effective practices in software engineering. A short document, written at the moment of decision and stored beside the code, preserves the context that would otherwise be lost, prevents repeated debates, speeds up onboarding, and supports honest evolution of the system.
To adopt the practice, start small. Pick a simple template, create a folder in your repository, and write the next significant decision as an ADR, including alternatives and negative consequences. Review it through a pull request, link it to the code and principles it affects, and mark it superseded rather than editing it when things change. Over time, the log becomes the memory of your architecture, and a shared language for making decisions openly, quickly, and with a clear view of the trade-offs.