AmouAI Hub/Courses/Software Engineering for AI Engineers/Chapter 16
Decisions on the Record, and the Spec the Agent Reads
A decision nobody wrote down will be made again, differently, by someone who does not know it was ever made. That has always been expensive. It is now sharper, because your codebase has acquired a second reader that works quickly, reads everything, has no memory of the meeting, and will helpfully undo anything the code does not explain.
By the end of this chapter you can
- Write an ADR whose context section is worth reading in two years
- Say which question a diagram answers, and delete the ones that answer none
- Recognise the code an agent will “fix” and protect it in the right place
- Detect the ambiguity words that make a requirement untestable
- Write a spec precise enough to produce the same system twice
- Choose a documentation depth proportionate to the decision
1An undocumented decision is re-litigated forever
Every system contains choices that look arbitrary and were not. Each one is a trap for the next person, and the trap resets every time somebody new arrives.
The invoice numbers skip 1,000 at a time because a customer’s accounting system chokes on consecutive ids. The retry limit is three because at five the payment provider rate-limits you. The webhook payload includes a field nothing reads because one integrator built against it in 2022 and has not migrated. None of these is visible in the code, all of them look like mistakes, and each will be “cleaned up” roughly once a year, breaking something, until somebody writes down why.
The cost of not recording a decision is not the documentation you did not write. It is:
- The same argument, repeatedly. Every new engineer proposes the alternative that was already rejected, and nobody can remember why it was rejected, so it gets discussed from scratch.
- Reversals by accident. Somebody removes the odd-looking thing, and the reason it existed reappears as an incident.
- Decisions that cannot be revisited. If nobody knows what a choice was for, nobody can tell whether the reason still applies — so the choice becomes permanent by default, which is the worst possible reason for anything to be permanent.
That last point is the one people underrate. Recording a decision is not about defending it forever; it is what makes changing it possible. A written reason can be checked against the present and found to no longer hold, and then you get to make a new decision deliberately — which is precisely what an unwritten reason forbids.
2The ADR, and why the context section is the valuable one
An architecture decision record is one page with four sections. Three of them are easy and one of them is the whole point.
| Section | What goes in it | Common failure |
|---|---|---|
| Status | Proposed, accepted, superseded by ADR-014 | Never updated, so the record and the system disagree |
| Context | What was true when the decision was made: constraints, scale, team, what was not known | Skipped, or written as a summary of the decision rather than the situation |
| Decision | What was chosen, in one paragraph, in the active voice | Too long, and mixed up with the context |
| Consequences | What this makes easy, what it makes hard, what you gave up | Only the good ones, which makes the record an advertisement |
Context is the section that ages into value. The decision itself is usually visible in the code, so in two years the interesting question is not what was chosen but whether the reasons still hold — and only the context can answer that. “We had five engineers, no infrastructure specialist, 8,000 organisations and one Postgres instance” is checkable against the present in ten seconds. “We chose a modular monolith for simplicity” is not: simplicity is still nice, so the record cannot tell you anything, and the argument starts again.
Two more things belong in a good context section and are almost always missing. What you did not know — “we had no measurement of retrieval quality at the time” — because it tells a future reader exactly what new information should trigger a re-examination. And what you rejected and why, in one line each, because that is the section that stops the same three alternatives being proposed every year.
The last criterion carries the most weight for a reason. A decision record that lives in a wiki nobody
opens does not prevent the cleanup in section one, because the person about to remove the odd increment
has no reason to search for a document about it. A three-word comment — // see ADR-011
— beside the line is what connects the two, and it is also the form an agent reads, because it is
in the file it was asked to change.
3Diagrams that answer a question
Most architecture diagrams are decorative. The test is simple: what question does this answer, and could you answer it faster without the picture?
The C4 idea — a few levels of zoom, each answering a different question — is useful mostly because it forces you to say which level you are at. A diagram mixing levels answers nothing, because every reader is looking for something different and none of them find it.
| Level | The question it answers | Worth drawing when |
|---|---|---|
| Context | What is this system, who uses it, what does it talk to? | Almost always. One box in the middle, users and external systems around it. Ten minutes, and it is what a new joiner needs on day one |
| Container | What is deployed, and what talks to what? | Almost always, and it is the one that goes stale fastest — date it |
| Component | What are the modules inside one deployable? | Occasionally, for the one part everyone gets lost in |
| Code | What are the classes? | Essentially never. The code is right there and the diagram is wrong within a week |
The failure mode worth naming is the stale diagram that is trusted. A diagram nobody believes is harmless; a diagram from eighteen months ago that looks authoritative sends people to components that no longer exist and hides two that do. The defences are unglamorous: put a date on every diagram, keep the source next to the code so a pull request can change both, and delete anything nobody has looked at in a year rather than maintaining it.
The strongest version of this is a diagram generated from something true — a dependency graph, a deployment manifest, a trace. It cannot go stale, and its ugliness is a feature, because nobody mistakes it for a considered view when it is really just the truth.
4Your codebase has a second reader now
Everything above was true before coding agents. What changed is the speed at which undocumented reasoning gets acted on, and by whom.
A human engineer who meets an odd-looking line hesitates. They ask in a channel, or leave it alone because it looks deliberate, or notice that git blame points at somebody senior. That hesitation is a safety mechanism made of social context, and an agent does not have it. It reads the code, forms a correct local judgement, and acts — across twenty files, in one diff, in ninety seconds.
This is not an argument against agents; the local judgement is usually right, and the speed is the point. It is an argument that the reasoning has to be where the code is, because the reader that matters now has exactly the context you put in the repository and nothing else. Three specific consequences:
| What used to be enough | What is needed now |
|---|---|
| A shared understanding that a module is performance-sensitive | A comment saying so, and a test that fails if the optimisation is removed |
| “Everyone knows we do it this way here” | A written convention in the repository, in a file agents are told to read |
| A deliberate inefficiency that looks wrong | A comment naming the reason and pointing at the decision record |
| Reviewers who catch the odd change | The same reviewers, now reading much larger diffs, plus tests that encode the intent |
Notice that every row on the right is something you would have wanted anyway. This is the reassuring part of the chapter: nothing here is a new discipline invented for agents. It is the existing discipline of writing down why, with the grace period removed — the practices that were merely good hygiene when the only reader was a colleague who might ask are now load-bearing.
And the test is the strongest of the four mechanisms, because it is the only one that is enforced rather
than merely available. A comment can be read and disagreed with; a failing test stops the change. If a
deliberate inefficiency matters enough to protect, the honest form of that protection is a test named for
the reason — test_invoice_numbers_are_not_consecutive_for_import_compatibility —
which is documentation that runs.
5Spec-driven development
The bottleneck in agent-assisted work is not how fast code is produced. It is how precisely the intent was stated, and imprecision now costs a whole implementation rather than a conversation.
The same instruction produces wildly different systems depending on how much is specified, and the specification effort is not linear in value — there is a level, well short of exhaustive, where almost all of the benefit arrives. Move through the levels below and watch where the problems stop being about correctness and start being about detail.
The pattern in the costs column is the useful finding: total time is roughly flat across levels two, three and four, and what changes is where it is spent. Vague specification does not save effort; it moves effort from writing into reviewing and repairing, where it is slower, more frustrating, and done by someone who now has to reconstruct what was meant.
An AGENTS.md at the root of the repository is where the repeated part of a spec goes. It is
read by agents and by humans, it lives with the code, and it turns level two from a paragraph you write
every time into a sentence that points at it. What belongs in it:
6Build: the spec that produces the same system twice
A useful test of a specification: if two competent implementers worked from it separately, would you accept both results? If not, the difference is what the spec failed to say.
This is a practical exercise rather than a thought experiment, and it is cheap to run: hand the spec to an agent twice in fresh sessions and compare the diffs. Everywhere they differ is either a detail you did not care about — fine, and now you know — or an assumption you left to chance.
The things that reliably differ, in rough order of how often they cause a problem:
| Left unsaid | What happens |
|---|---|
| Error behaviour | One implementation returns 404, the other 422, a third returns 200 with an error field |
| Concurrency and retries | One is idempotent, one is not, and the difference only appears under load |
| Boundary values | Empty lists, zero amounts, exactly-at-the-limit — each handled differently |
| What happens on partial failure | One rolls back, one leaves the successful part, one retries forever |
| Performance expectations | One implementation is a clean N+1 that nobody notices for a year |
| Security boundaries | The tenant filter is applied in one and forgotten in the other |
A specification that names those six is not long — it is a paragraph and a handful of test cases — and it is close to the full value of level four in the widget above. That is the practical summary of this chapter: write the awkward cases down, put the conventions in the repository, and let the shape of the code be figured out by whoever implements it.
The connection back to the rest of the chapter is that a specification and a decision record answer different questions and both are needed. The spec says what the system must do; the record says why it is that way rather than the obvious alternative. An agent handed only the spec will produce something correct and will confidently normalise away every deliberate oddity, because nothing told it which oddities were load-bearing.
✓Checkpoint
▶Playground
A different task, four levels again. Watch where the problems change character — from “this is wrong” to “this is a detail nobody stated” — and note which level that happens at.
✓Exercise set
Twelve problems. Most of them build tools that check writing rather than code — an ADR completeness checker, an ambiguity detector, a spec-to-test generator — because the artefacts this chapter is about are the ones nobody currently lints. Your work is saved in this browser.
Chapter 17 — A Testing Strategy Worth the Runtime
Part 4 begins. Coverage is not a goal and the pyramid is not a law. Next chapter: which tests are worth their runtime, and how to test a system whose output is different every time.