The Architecture Decision Record: A Template That Engineers Actually Fill In

#architecture decision records
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Founder & Lead Developer

Expert in software development and legacy code optimization

Every engineering team that has inherited a codebase knows the question that has no answer: why is it built this way? Someone chose Postgres row-level security over application-layer tenant checks. Someone picked a message queue for a workload that a cron job would have handled. The person who made the call has left, the pull request description says "refactor auth," and the reasoning is gone. Architecture decision records exist to solve exactly this problem, and almost no team maintains them for longer than a quarter.

The reasons are predictable. The template is too long, the process feels like ceremony, and the historical context decays until the records become a graveyard of decisions nobody reads. So teams either skip ADRs entirely or adopt a heavyweight process that collapses under its own weight within a few sprints. Neither outcome preserves the one thing that matters: a durable record of why a decision was made, what alternatives were weighed, and what you accepted as a consequence.

This post is the practical counterargument. A lightweight ADR process that survives contact with a shipping team looks nothing like the elaborate templates you find in most guides. It is short enough to write in the time it takes to describe the decision out loud, stored close enough to the code that it does not rot, and triggered by clear rules so nobody argues about whether a given choice deserves one.

What Architecture Decision Records Are For

An architecture decision record is a short document that captures one significant technical decision: the context that forced the choice, the decision itself, the options you rejected, and the consequences you accepted. The value is not in the document. It is in the reasoning the document preserves.

Consider the difference between two kinds of knowledge. "We use Symfony Messenger with a Doctrine transport" is a fact you can read off the codebase in thirty seconds. "We chose Doctrine transport over RabbitMQ because our throughput is under 500 jobs a minute and we did not want to operate a broker for a workload that fits in Postgres, accepting that we will need to migrate if volume grows past that ceiling" is reasoning you cannot reconstruct from the code at all. The first is discoverable. The second is lost the moment the author forgets it, and they will forget it.

That is what ADRs protect. Not the decision, which the code already encodes, but the shape of the thinking around it: the constraints that were real at the time, the tradeoff that was deliberately made, and the condition under which the decision should be revisited. When a new engineer asks why the system works this way, the honest answer is usually "there were three options and we picked this one for reasons that made sense in March." An ADR is where those reasons live.

The Minimal Template

Most ADR templates fail because they ask for too much. Status, deciders, consulted parties, informed parties, technical story links, positive consequences, negative consequences, neutral consequences, and a pros-and-cons table for each option. By the third field the author has decided this is not worth it, and they are right.

The template that teams actually fill in has four parts and fits in five lines:

# ADR-014: Doctrine transport for Symfony Messenger

Context: What forces a decision now? The real constraint, not background.
Decision: What we are doing. One sentence, active voice.
Options considered: What we rejected and the one reason each lost.
Consequences: What we accept as a result, including the revisit trigger.

That is the whole thing. The discipline is in the "options considered" line, because that is the field that captures what you gave up. A decision recorded without its alternatives is just documentation of the status quo. A decision recorded with its rejected options tells a future reader that RabbitMQ was on the table, why it lost, and therefore what would have to change for the answer to flip.

Number the records sequentially and never reuse a number. When a decision is later reversed, you do not edit the old ADR. You write a new one that supersedes it and add a single line to the old record pointing forward. The chain of superseded decisions is itself valuable history: it shows how the system's constraints evolved.

What Triggers an ADR Versus a Pull Request Description

The most common failure mode after adoption is over-recording. A team that writes an ADR for every non-trivial change drowns in records and abandons the practice within a month. The opposite failure, recording nothing, is just the original problem. The line between them is a simple test.

Write an ADR when the decision is expensive to reverse and affects more than the code in front of you. Choosing an authentication library, an API style, a tenancy model, a deployment target, or a logging format are all decisions that ripple across the codebase and cost real money to undo. Those get an ADR. Renaming a service, extracting a helper, or restructuring a component tree lives fine in a pull request description, because reversing it is cheap and the blast radius is local.

A useful heuristic: if you would want to know the reasoning two years from now when the person who made the call is gone, it is an ADR. If the git blame and the PR description are enough, it is not. Decisions about your overall architecture and technical direction, the kind we help teams work through in our tech stack strategy engagements, are almost always in the first category. They shape everything built afterward, and they are exactly the decisions whose reasoning evaporates first.

Where to Store Them So They Do Not Rot

ADRs die when they live far from the code they govern. A Confluence space, a shared drive, a Notion database: all of these start well and decay because nobody updating the code is looking at them. The fix is to store ADRs in the repository, as Markdown, in a docs/adr/ directory that ships with the code.

Proximity is what keeps them alive. When the records are in the repo, they show up in code review, they are versioned alongside the change that motivated them, and an engineer reading the auth module can find the auth ADR without leaving their editor. The record and the code move together through every branch, merge, and revert. A decision reversed in code is a decision whose ADR is right there to supersede.

There is a second benefit to keeping ADRs in the repository: they become reviewable. An ADR submitted as part of a pull request is a conversation, not a decree. Reviewers can push back on the reasoning before the decision is locked in, which is precisely when that feedback is cheap. This is one of the things a rigorous code quality consulting review looks for, whether the significant decisions in a codebase were reasoned about in the open or made unilaterally and discovered later by whoever inherited the consequences.

Documenting Past Decisions Without an Archaeology Project

Teams adopting ADRs on an existing codebase often talk themselves out of it because they imagine reconstructing every historical decision. That is the wrong goal and it will exhaust the team before they have written a single record that helps. You do not need the history. You need the decisions that are still load-bearing.

Start with the questions new engineers actually ask during onboarding. Why this database? Why this deployment setup? Why is authorization enforced here and not there? Each of those questions maps to a decision that is still shaping the system, and each answer is an ADR worth writing retroactively. Write five of them, dated with the approximate original decision date and a note that they were reconstructed after the fact. That is enough to cover the decisions people keep asking about, and it costs an afternoon rather than a sprint. This is also the fastest way to get productive on a system you did not build, which is why it shows up in our legacy code optimization work.

From that point forward, the process is purely additive. You write a new ADR when a triggering decision comes up, and the collection grows organically around real choices rather than a backfill project nobody has time for.

The ADRs Worth Writing First

In the Symfony and Next.js SaaS projects we work on, a handful of decisions come up on nearly every codebase and repay the effort of recording more than any others. The authentication and authorization approach, whether you rolled your own or adopted a library, is the single most valuable ADR because it is the hardest to change later and the most dangerous to get wrong. The API style, REST versus GraphQL versus something in between, shapes every integration built afterward. The tenancy model, shared schema versus schema-per-tenant versus database-per-tenant, determines your isolation guarantees and your scaling ceiling. The deployment target and the logging format round out the set.

If you write only five ADRs, write those five. They cover the decisions that new engineers ask about, that acquirers examine during due diligence, and that cost the most to unwind when the reasoning behind them has been lost. Everything else is a bonus.

The point of an architecture decision record is not documentation for its own sake. It is a small, deliberate investment in your future team's ability to change the system with confidence rather than fear. A codebase whose major decisions are recorded is a codebase you can evolve. One whose decisions are folklore is one you can only guess at.

If you are inheriting a system whose reasoning has gone dark, or standing up an engineering practice that will outlast its founders, we help teams put exactly this kind of structure in place. Reach out at hello@wolf-tech.io or read more about how we work at wolf-tech.io.