Template

Architecture decision record

Keep the reasoning behind a design decision available to the next reviewer, maintainer or incident responder.

Seven consistent headings cover the context, goals, limits, design, failure cases, alternatives and open questions. The completed example records a decision about letting a system move money.

Use it when a decision has been made and somebody will have to understand it later, during an incident, at a handover or after its author has moved on.

The blank

Copy the headings into your working document and answer each prompt. Keep unanswered decisions visible for the review.

Decision: ⟨title⟩
Date: ⟨date⟩
Author: ⟨name⟩
Status: ⟨proposed, accepted or superseded⟩

  1. Context

    What breaks today, cited against something you actually watched, with the number.

    Not background. The specific thing that failed, and what it cost when it did. If you are writing before anything has failed, say so. An anticipated failure is a legitimate context and a different kind of claim.

  2. Goals

    Three at most, each one testable. Could somebody write a check that passes or fails on this without a person judging it?

    "Safer" is not a goal. "No order is refunded twice" is a goal, because you can go and look. If it is not checkable, it is a wish, and it will not survive contact with the review.

  3. Non-goals

    What you are not fixing, and why that is acceptable this quarter.

    The thing a reviewer will ask about that you have decided, on purpose, to leave alone. A non-goal nobody can quite justify is very often the seam where the design is unresolved.

  4. The design

    List checks in execution order. For each failure, specify whether the system refuses, escalates or asks a person. Say where the state lives.

    Order matters, and so does where the state lives. If it lives in memory, a restart undoes it.

  5. What can go wrong

    One row per case: what arrives, what your rule does, what the customer sees.

    Include the case where your own guard is wrong. A rule that silently refuses a legitimate request has swapped one failure for another, and that one is invisible unless you go looking for it.

  6. Alternatives

    One you seriously considered, and the specific reason it lost.

    Anyone can state a decision. The rejected options are where the judgement is visible, and the alternatives help a reviewer examine the judgement. "Use a better model" counts, and rejecting it well is most of the work.

  7. Open questions

    What you could not settle. One line is enough.

    The part you would want a second opinion on. Leaving it empty because it looks better empty is how an unresolved thing becomes a decision nobody remembers making.

The Markdown file is the blank with every prompt as a quoted block you delete as you answer it.

A completed example

Illustrative scenario, not a claimed customer incident. This example follows the returns assistant from recommendation to refund authority. Fields in ⟨angle brackets⟩ are for your own design and measurements.

Refund authority for the returns assistant

Written after the review in the previous template, by the engineer who owns the design. It is the same decision seen from the other side: the agenda is what the room asked, and this is what the author concluded and is prepared to be argued with about.

Decision: Refund authority for the returns assistant
Date: ⟨date⟩
Author: ⟨the engineer who owns the design⟩
Status: Proposed

  1. Context

    The assistant recommends refunds; a person issues them. In ⟨n⟩ requests sampled over ⟨period⟩, ⟨n⟩ recommendations were correct and were typed into the refund system by hand, ⟨n⟩ minutes after the assistant produced them. ⟨replace these with your own counts, this example asserts no measurement⟩

    Two things follow. The queue is the delay customers feel. And the person typing is not re-deciding: they are transcribing, which means the control everybody believes exists is not really there.

  2. Goals

    1. No order is refunded twice, including across separate conversations.
    2. No refund is issued above the amount the cited policy clause allows.
    3. Every refund can be traced afterwards to the clause and version it was made under.

    All three are checks somebody can run against the ledger and the run records, without a judgement call.

  3. Non-goals

    Not fixing the policy itself, which contradicts itself in two places. It is a content problem with a content owner, and the design routes those cases to a person instead.

    Not handling partial shipments automatically. They are ⟨n⟩% of volume and the state is genuinely ambiguous; they hand over.

    Not reducing the refund window or changing what customers are entitled to. This decision is about who executes the policy, not what it says.

  4. The design

    issue_refund receives an order id, an amount, a policy clause and its version. It verifies the request through four checks, in this order:

    1. Order exists and is in a refundable state. Read from the order service. Fails → refuse, and hand over with the state named.
    2. Clause permits a refund for this order. Re-read at the cited version, not the current one. Fails → refuse, and raise as a content problem.
    3. Amount is within what the clause allows. Fails → refuse. Never clamps silently to the maximum; a clamped refund is a different decision wearing the same name.
    4. No prior refund against this order. Read from the refund ledger, which is the payment system's own table and not a cache. Fails → refuse and hand over.

    The refund ledger lives in the payment system. Each request checks that shared record, including after a restart or from a separate conversation. Concurrent requests also need an atomic duplicate check and refund operation, or equivalent idempotency control; a ledger lookup alone does not prevent a race.

    On timeout, the tool does not retry. It records the attempt and hands over, because a timeout is an unknown outcome rather than a failed one.

  5. What can go wrong

    What arrivesWhat the rule doesWhat the customer sees
    Same order, second conversationLedger check refusesThe refund already issued, with its date
    Amount above the clauseRefuses rather than silently reducing the amountHandover to a person, with the clause quoted
    Persuasive message asks for an exceptionTool re-checks the clauseHandover to a person
    Refund tool times outRecords, checks the attempt and hands over without blind retryA wait and a person who can see the attempt
    Legitimate request refused by a wrong guardRecords the refusal and reasonA distinct message and a route to a person

    The last row is the one this design is weakest on. The refusals are recorded, but nobody currently reads them, so a guard that is too tight would look exactly like a guard that is working.

  6. Alternatives

    Keep a person in the loop and make the queue faster. This was the strongest alternative and it very nearly won: it changes no authority at all. In this illustration, the person is transcribing rather than reviewing. Making that queue faster would leave the same gap in authority.

    Put the limits in the system prompt and connect the tool as it is. Rejected. The prompt is a message inside the system and competes with everything else in the context, including the customer's own words. The tool is where the money moves.

    A stricter model with better instructions. Rejected as the authority control. A different model may change behaviour, but it does not replace the checks on order state, policy, amount and prior refunds.

  7. Open questions

    What the duplicate check should do when the ledger read itself fails. Refusing every refund during a ledger outage is a poor customer outcome; proceeding is how you pay twice. Needs the person who owns the ledger.

    Who reviews refusals, and at what interval? Relying only on complaints may leave an overly strict guard unnoticed.

The worked example as a Markdown file, with its illustrative label.

How to use it

Write it after the decision and before the work, while the alternatives are still fresh enough to be stated fairly. A record written afterwards tends to describe what was built as though it were inevitable, and the rejected options quietly become straw.

The seven headings are fixed and are worth keeping fixed. Their value is that a record written this month can be read against one written last year without anybody first working out how the two documents are organised. What varies is the decision above them, not the shape.

Two of the seven do most of the work in a review. Goals must be testable, and the fastest way to find an untestable one is to ask what check would fail if the goal were not met. What can go wrong is one row per case, and the row people leave out is the one where their own guard is wrong.

Keep it beside the code it describes rather than in a document store nobody opens. A decision record that cannot be found during an incident is a record of a decision, not a working one.

What it will not do

Use it to preserve the argument behind the decision. Link the detailed design and specification rather than copying them into the record.

It also does not decide anything. If the decision is contested, the record makes the contest legible; it does not settle it. That is the review’s job, and there is an agenda for that.

Make the record usable at the next review

Pair the record with the Design review agenda so the next conversation can start with the decision and its unresolved questions.

To develop and defend those decisions while building a working system, compare the Agentic Systems programmes.

File Download

Get the file

The Markdown file is the template as it reads on this page: the blank with every prompt as a quoted block you delete as you answer it, or the worked example with its illustrative label. It is built against a name, a role and an email address.

This file is the same for everyone. Nothing you typed goes into it. Your address is used to send you this resource once. It does not start any other email.