Skip to content

14 min read

Evidence in a decision record

On this page

An architecture decision record, or ADR, is the short document a team files to say which structural option it picked and why. It is read twice: once by a reviewer deciding whether to approve, and once, much later, by somebody deciding whether the thing still deserves to exist. Both readers need facts they can check, and the second one has no access to the meeting.

The failure mode this chapter is about is not a false statement. It is a true statement that carries no information about the integration in front of you, and it appears in these records more often than any other single line: the observation that MCP is the standard interface for agent tooling. Where a company has ratified the protocol in an architecture forum, that sentence is correct, uncontroversial, and equally true of every integration anybody will ever propose there — including the ones that should not be built.

A justification that fits the rejected proposal too

Here is the whole of a record that came back rejected, and nothing in it is false:

markdown
# ADR-114: Expose inventory lookup to agents

Status: proposed
Decision: build an MCP server.
Rationale: MCP chosen because it is the standard interface for agent
integrations.

Take the rationale line out and paste it into the ticket-summariser record from chapter 1, where a direct function won on lines, failure modes and latency. It reads exactly as well. It would have read as well in a record proposing a server for a capability with no consumers at all. A statement that supports the accepted decision and the rejected decision identically has not distinguished between them, which is the property a justification has to have in order to be one.

Why that property, rather than truth, is the one a record needs takes a single step to see. A record exists to explain why one option was taken and another was not, so every line in it is doing comparative work whether its author meant it to or not. A sentence equally true of the chosen option and of the rejected one cancels out of that comparison and leaves whatever else is in the record carrying the whole decision on its own. Being correct and being discriminating are separate properties of a statement, and only the second one is the job. The first is a prerequisite that gets mistaken for it, which is how a rationale can be unimpeachable line by line and still hand a reviewer nothing to weigh.

The usable test is whether the fact could have come out the other way for this integration and changed the answer. "The protocol is standard" is fixed across the company, so it cannot come out the other way for any individual case. "The Growth team's churn-risk agent is integrating in Q3, tracked as GRW-412" could have come out as "no consumer outside our deploy boundary", and the decision would have flipped. That is what makes it evidence.

Statement in a recordTrue?Could it be false for this integration?Verdict
MCP is the industry standard for agent toolingyesno — it is true of every integrationnot evidence
Any MCP-capable client can consume ityesno — a property of the protocolnot evidence
Warehouse team owns the stock ledger and is audited on ityesyesdecisive on ownership
Two teams have expressed interestyesyesforecast, not a consumer
Ops' replenishment agent ships from Ops' repo, integrating Q3yesyesdecisive on reuse
We already run three MCP servers on the shared platformyesyesevidence, but about cost

The last row is the one people miss when they overcorrect. "The protocol is standard" is not evidence; "we have an internal platform where a fourth server costs two days of chart work instead of three weeks of scaffolding" is, because it is a fact about your company that could be false at the company next door, and it moves the arithmetic that chapter 5 sets up. The standard becomes relevant through its consequences on your cost line rather than through its status.

That row follows from the same rule as the ones above it, applied against a different set. A fact earns its place by varying across whichever set the decision is being taken against. The state of an industry varies across nothing a reviewer can reach — not across the proposals in front of them and not across the companies they might have been sitting in. An internal platform is fixed across the proposals in front of them and emphatically not fixed across those companies, which is the set that matters once the question has narrowed to what a fourth server would cost. A cost line is drawn against the alternative of building the scaffolding yourself, and that alternative has a different price in every building.

Being evidence about cost is also a limit on what the row can settle, and the limit is worth stating because this is the line people over-read once they have learned to distrust the other one. A cheap platform lowers the price of a boundary. It does not supply a consumer, an owner or a credential, so it can move a marginal case to a positive one and it cannot move a case with no reuse, no ownership constraint and no security boundary anywhere at all — there is nothing there for a lower price to be the price of.

The reason the standards line gets written so often is that it does real work of a kind that has nothing to do with the design. It is true, it cannot be contradicted, it aligns the author with a decision the architecture forum already ratified, and it converts a question about this integration into a question about compliance with a policy, which is a much easier conversation to win. Reviewers feel the shift and often go along with it, because objecting sounds like objecting to the standard. Naming what happened is usually enough to reset the discussion: the standard settles which protocol you use if you build a server, and the record has to establish that a server should exist.

That sentence is carrying more than its length suggests, and the reader most likely to go wrong from here is the one who concludes that facts about the industry never belong in a record. Two questions sit in front of the reviewer and they are asked in order. Should this capability sit behind a boundary at all, and if it should, what should the interface be? The standard is decisive for the second and inert for the first, because it varies across interface choices and does not vary across capabilities. Evidence is always evidence for some particular question, so a fact is not disqualified by being general — it is disqualified by being general with respect to the question actually on the table. A record that answers the second question and files the answer under the first has said nothing false; it has answered something nobody asked.

What each of the five fields has to contain

A record that has to survive review states five things about the integration, and each of them has a recognisable failure mode where the author writes a sentence that sounds like an answer without being one.

FieldWhat gets writtenWhat has to be written
Client reuse"several teams will use it"who they are, which repos they ship from, and when they integrate
Ownership"the platform team will own it"who decides what correct means, who is audited on it, who deploys it
Change rate"fairly stable"non-additive interface changes in the last six months, and the slowest consumer's release cadence
Security boundary"it will be authenticated"what the caller holds today, what it would hold after, and what the boundary takes away
Rejected alternatives"considered and rejected"the cheaper option named, priced, and the fact that ruled it out

The security row is the one where a true sentence most often stands in for the relevant one. Every server is authenticated; that describes the server rather than the boundary. What a reviewer needs is the before-and-after on the caller's reach. If the agent already holds a database credential covering the same rows, the answer is that the boundary takes nothing away and the field is empty, which is a legitimate thing to write. If the agent currently holds a provider key with refund scope and would afterwards hold a token permitting one capped operation, the field carries the decision on its own.

The client reuse row fails in a quieter way, because "several teams will use it" is not vague so much as unfalsifiable — there is no observation next year that could show it to have been wrong. Naming the repositories is what makes it checkable, and it does something else as well: a consumer's repository is the shortest description of whether it can embed your code, so the field answers the deploy-boundary question and the reuse question in the same line. A record that lists team names without repositories has usually not asked.

The change rate row asks for two quantities where an adjective would fit, and the pair is chosen so that multiplying them gives you something. How many non-additive interface changes there were in the last six months sets how often a dual-serving window has to be opened — a period during which the server returns both the old field and its replacement, so that consumers can migrate without breaking. The slowest consumer's release cadence sets how long each window has to stay open, since a window cannot close until the last consumer is off the old field. Multiply the count by the length and you have an upper bound on how much of the year the server spends inside at least one window; windows that overlap cost less than their sum. That fraction is the coordination burden the word "stable" was standing in for, and neither number produces it alone — frequent changes with a weekly consumer are cheap, and rare changes with a quarterly consumer are not.

Rejected alternatives is the field authors treat as paperwork, and it is the one that tells a reviewer whether the decision was made or arrived at. Naming the alternative and pricing it demonstrates that the author considered a world in which they did not build the thing they wanted to build, and it saves the next reviewer's time, because the cheaper option is the first thing anyone reading the record in six months will propose.

Pricing the alternative is the half that gets left off, and a named-but-unpriced alternative fails in exactly the way "several teams will use it" fails. "We considered a published package and it did not fit" contains no quantity, so there is nothing in it for a reviewer to dispute and nothing that could later turn out to have been wrong. Attach a cost and a disqualifying fact — the package would have been cheap to publish, and Ops' agent is written in Go — and both halves become attackable, which is the same as saying both halves are carrying information. The field is not asking whether the author thought about the alternative. It is asking what the alternative would have cost, because that is the figure the chosen option had to beat.

For most integrations there are three cheaper options and they are all worth a line. The direct call inside your own service is the baseline, and if it was rejected the record needs the reason in a form that can be checked — a consumer outside the deploy boundary, an ownership constraint, or a credential. A published package is the option that gets skipped most often; it costs a version matrix and gives you no operational surface, and the reason to reject it is usually that the capability must be centrally controlled or that not every consumer runs your language. An existing internal HTTP API, where one exists, makes the MCP server a thin adapter rather than a service, which changes both the cost and the ownership answer, as the entitlements adapter in chapter 3 shows.

There is a fourth family of options that will not fit in this record yet, and it is honest to say so rather than to leave the impression that three were all there were. Skills and subagents solve overlapping problems with a different cost structure, they arrive at stage 12, and a record written today can note the comparison as deferred. If you want the outside view before then, the Auth0 piece What AI Tools, MCP Servers, and Skills Actually Do separates the three mechanisms and their real costs cleanly enough to be worth twenty minutes.

The one integration where the standard is the argument

There is a shape of integration where you genuinely cannot fill in the client reuse field with names, and it is the case the standards argument was invented for. If the consumers are outside your company — a customer pointing their own assistant at your product, a partner building an agent against your API, an integration marketplace — then enumerating them is not merely difficult, it is the wrong thing to attempt, because the value of the interface is precisely that you do not have to know who is on the other end. A payments company shipping a public MCP server is in that position, and so is any vendor whose customers arrive with clients the vendor did not write.

Working out why the attempt is wrong rather than merely hard is what tells you what replaces it. A named list can only ever contain the consumers already known about, and the ones a public interface is built for are the others — the day nobody can attach an assistant your company has never heard of is the day the interface has stopped doing its job. A list would therefore describe the least valuable part of what is being shipped, and a record that committed to it would be committing to the wrong quantity rather than to an incomplete one. Whatever takes the field's place has to vary the way a named consumer varies, without claiming a completeness the design deliberately rules out.

The record for that integration still has facts in it; they are just facts of a different kind. How many customers have asked, and in what form — support tickets, sales calls, a competitor shipping one? What does the existing public API cost a customer to integrate against today, and how much of that does the protocol remove? Which clients do you commit to testing against, since "any MCP-capable client" is a claim you will be held to during an outage? Who answers when a customer's client sends a request your server rejects, and is that the support organisation or an engineer? Those questions have answers that could come out either way, and a record that answers them is doing the same work as the revised ADR-114 with a different population of consumers.

What does not transfer is the inference. The public case removes the obligation to name consumers; it does not remove the obligation to produce facts that could have come out otherwise, and it replaces one list of checkable claims with another. A record that cites the standard and stops has not reached the easier version of the problem — it has skipped both.

The record's real reader is fourteen months away

Records get written for the reviewer in the room and read by somebody else entirely. The person who opens ADR-114 next year is deciding whether the server still deserves to exist, whether a fifth consumer changes anything, or why a capability they have just inherited works the way it does. A record that says "MCP is the standard" hands that person nothing to check, so they either keep the server out of caution or reopen the whole argument from scratch, and both of those outcomes are expensive.

The five fields survive contact with time because each one is a claim about the world that can be re-examined. The Ops integration either happened or it did not. The dual-serving window either held at four weeks or it stretched. The token either removed supplier pricing from the agents' reach or somebody quietly granted the replica credential back during an incident. A record written this way is a set of predictions, and the next chapter is about the prediction people get most confidently wrong: the one where somebody multiplies four teams by two weeks each, compares it to six, and concludes that the arithmetic favours the server.

When MCP is the wrong answer — read the current version at https://daviddominguez.dev/learn/en/when-mcp-is-the-wrong-answer/evidence-in-a-decision-record

Back to top