Chapter 8 of 18

The brief

How to say what you want: specs, acceptance criteria, plans, slices, and the questions to ask first.

An agent will do something with whatever you type. What matters is what it fills in for itself, because everything you leave out it decides, quickly and without telling you, and then it writes working code for its decision. A brief is where you take those decisions back.

We measured how much that matters. One small model, eight one-function tasks, a vague brief against a brief with the right words in it, 15 runs of each. On seven of the tasks there was almost nothing in it: the vague brief passed every hidden scenario, a check the model never saw, in 101 of 105 runs, the worded one in 102 of 105. On the eighth, payment-events, which keeps an order's status straight as payment messages arrive, the vague brief passed 27% of the time and the worded one 100%. Adding mechanical gates, automatic checks that send the work back, only moved the vague brief to 33%, because they can't catch what nobody asked for. A neutral reviewer reading the code rejected all 15 vague runs of that task, and none of the worded ones, so a reader sometimes can.

Both halves of that are the finding, and neither shows that words always win: this was one small model, with one-function tasks and no real repo. So this chapter doesn't ask for longer briefs. Most of what you could write, a decent model would have done anyway. The skill is finding the one sentence it wouldn't have, and that takes knowing what the edges of the job are called.

Then there is a real disagreement. Some people who ship a lot with agents say the spec is now the true source and code is what comes out of it. Others, just as serious, say they don't believe in planning and that the best spec is code. We set out both, and say when each is right.

Brief

What you tell an agent before it starts: what to build, why, what counts as finished, and whatever it couldn't work out by itself. It can be one sentence or a page. Anything it leaves out, the agent decides for you.

A colleague asks when a request is unclear. A model usually picks an answer, writes working code for it and reports success, so you learn what it chose only by reading the result. The brief is the cheapest moment to make those choices yourself.

Say it to your agent

“Add dedupe to the payment webhook handler in src/webhooks/pay.ts. Webhooks can arrive twice, so a repeat must change nothing. Done means bun test passes with a new test for the repeat. Don't touch the refund code.”

Some briefs need to outlive the session they were typed in.

Spec

Also called Specification

A written description of what something must do, exact enough that two people, or two agents, would build the same behaviour from it. It says what and why, and usually leaves how to the builder.

A session forgets. A spec in the repo lets the next session, a reviewer and a second agent all hold the work to the same thing, and you can read it before any code exists. The price is that it is one more thing that can be wrong, and nothing tells you when it is.

Say it to your agent

“Write docs/specs/webhooks.md: what apply() must do when events repeat or arrive out of order, five example sequences with the result of each, and what is out of scope. No code yet. I will read it first.”

Requirement

One thing the result must do, stated so that someone could check it. "apply ignores an event older than the newest one for that order" is a requirement. "Handle events properly" is a wish.

An agent reads a wish as permission to decide. But plenty of real requirements change nothing either: on payment-events, every vague attempt dropped repeated events without being told. Spend your words on what the model wouldn't have done by itself.

Say it to your agent

“Rewrite these as requirements I can check: each one names the input and the exact result. Flag any line you can't turn into a check and ask me about it. Leave "should be fast" alone until I give you a number.”

A requirement says what you want. Acceptance criteria are how you will know you got it.

Acceptance criteria

The specific checks that decide whether this piece of work is finished, agreed before it starts. Each one passes or fails. The best are written so that a program can run them.

An agent's own tests check what it built, not what you needed. Of the 11 broken vague attempts in our runs, 9 passed every test they wrote for themselves. Criteria written before the code, by someone who knows the job, can't be shaped by the code.

Say it to your agent

“Before you write code, add these to tests/webhook.test.ts and show me them failing: a repeat of the same event id ships once; refunded at 2 then paid at 1 leaves the status refunded and ships nothing. Then make them pass. Do not edit the tests.”

Definition of done

The list that must be true before anything counts as finished, on every task: say the code type-checks, the tests pass and the docs are updated. You write it once, and it applies to all the work.

An agent stops when the work looks done to it. A written definition gives it something to run in place of that feeling. It only rejects what it names, though. The bench below shows how far a general list gets you when nobody asked for the behaviour that was missing.

Say it to your agent

“Add to AGENTS.md: "Done means bun run typecheck, bun run lint and bun test all exit 0, with the output pasted in your reply. If one could not run, say inconclusive, not done."”

Criteria say what must happen. The next three terms say what must not.

Scope

The edges of the job: which files, features and behaviours this task may change, and which it may not. One line of scope can save a long review.

Agents overshoot and undershoot, and you pay either way: too much is a diff you must read, too little is a follow-up. They can't tell how much you were hoping for. So say it, with something you can check, such as a folder and a rough size.

Say it to your agent

“Only change src/webhooks/ and its tests. If you think anything else needs to change, stop and list it in your reply instead of changing it. Aim for under 100 changed lines.”

Non-goal

Something that could reasonably be a goal and that you have chosen not to pursue this time. "No offline mode in this version" is a non-goal. "It shouldn't crash" isn't, because nobody was ever going to want crashes.

An agent reads a gap as room to build. Naming the reasonable thing you are leaving out stops it being built, spares you reviewing it, and tells the next person it was a decision and not something you forgot.

Say it to your agent

“Non-goals for this change: no admin screen, no email notifications, no migration of old events. If you find yourself building one of these, stop and tell me.”

Edge case

An input at the limit of what a program expects: nothing at all, exactly one, zero, the largest allowed, the same thing twice, things in the wrong order. Bugs gather here because the usual case is what everybody pictures.

Models handle the edges everyone has heard of and miss the ones particular to your domain. In our runs all 15 vague attempts handled a repeated webhook, and 11 of 15 got an old event arriving after a newer one wrong. Listing the edges costs one sentence.

Say it to your agent

“List the edge cases for this function before you write it: empty input, one item, duplicates, items out of order, very large values. Give the result you would expect for each, and ask me about any you are unsure of.”

Constraint

A limit on how the job may be done, rather than on what it must do: no new libraries, under 200 milliseconds, use the database we already have.

An agent takes whichever route looks most natural, and it has no idea what your project ruled out last year. Constraints are cheap to state and expensive to discover during review. Once one matters twice, turn it into a check.

Say it to your agent

“No new dependencies. Use the Map we already have, not a database table. It must handle 1,000 events a second on one server. If you can't meet one of these, tell me which and why before writing code.”

Examples

Also called Specification by example, Few-shot prompting

Showing what you want with concrete inputs and the results they must give, in place of or beside a description. A good one settles what a paragraph leaves open, and it is hard to read two ways.

Examples are direct, but most of them rule out very little. "Jan 15 gives Feb 15" leaves wrong programs standing that "Jan 31 gives Feb 28" removes at once. The useful ones sit at the edges, so you have to know where the edges are.

Say it to your agent

“nextBillingDate must do this: Jan 15 plus 1 gives Feb 15. Jan 31 plus 1 gives Feb 28. Jan 31 plus 2 gives Mar 31, not Mar 28. Feb 29 2024 plus 12 gives Feb 28 2025. Write these as tests first.”

This is the failure the experiment caught in the act. One innocent sentence, and the model read it three ways.

Ambiguity

A sentence that can honestly be read more than one way. A model rarely stops to ask which you meant. It picks a reading and writes working code for it, so nothing looks wrong.

Between people, ambiguity comes back as a question. From an agent it comes back as behaviour. "Updates an order's status" gave us three kinds of program. All of them type-check, and they agree on the obvious cases, so nothing tells you a choice was made.

Say it to your agent

“Before you start, quote back any sentence in this brief that could be read two ways, and give me both readings with an input where they differ. Do not pick one yourself.”

Have it interview you

Also called Interview me

Instead of writing the brief yourself, you ask the agent to question you until it could write it. It finds the decisions you hadn't noticed you were making.

You can't list what you haven't thought of. A model has read a great many specs for this kind of feature, so it is good at what must be decided and knows nothing about your answers. That split is the useful part: it brings the list, you bring the choices.

Say it to your agent

“I want to add rate limiting to the public API but haven't thought it through yet. Interview me until you could write the spec, then write it to SPEC.md.”

All of that was about what to build. Now the how, which is where people start to disagree.

Plan

The steps an agent intends to take, written before it takes them: which files, in what order, what it will check, and what it isn't sure about.

It is the cheapest place to find out the agent misunderstood you. It is also a guess that nothing has tested yet, and polishing one can feel like progress while no knowledge is being added. Skip it when the change is small and the approach is obvious.

Say it to your agent

“/plan Add idempotency to apply(). Read src/webhooks/ first. Give me the files you would change, the test you would write first, and what you are unsure about. If it is one file and obvious, say so and skip the plan.”

Plan as an artefact

Also called Plan file, Feature list

A plan saved as a file in the repo, with steps that get ticked off, so that it survives the session. A later agent can pick it up, and a reviewer can see what was meant.

A plan in a chat disappears with the chat. In a file, it carries work across sessions and makes drift visible. The hazard is the same as any document: a stale plan gets read as truth, so it has to change when reality does.

Say it to your agent

“Write the plan to docs/plans/webhooks.md as a checklist. After each step, tick it, commit, and add the command you ran with its output. If the plan turns out to be wrong, change the file first and tell me why.”

Here the people who do this for a living part ways. One camp says the spec is now the real source code.

Spec-driven development

Also called SDD, Specifier role

Working so that the spec comes first and stays the main thing: you write and review the spec, and agents produce the code from it. How far this goes varies, from a spec for one task to a spec that replaces code as what you edit.

It answers a real problem. Code is cheap, intent gets lost, and agents write more than anyone can read, so a spec gives you something smaller to check. The bill is long documents to review, and an agent that may still ignore them.

Say it to your agent

“Write the spec for this feature in specs/webhooks.feature, as Given/When/Then scenarios with real values. Stop there. I will approve it, and then a separate session writes the code and makes each scenario run as a test.”

The best spec is code

Also called Plan through code

The opposite position: write code early, because code that runs answers questions a document can only guess at. A working version or a prototype becomes the description of what you want, and anything written comes after.

Our reading, and only ours: code wins when running it shows you the answer, one person is deciding, and throwing it away is cheap. A written spec wins when being wrong won't show up on a run, as with money, when several agents must build to one thing, or when nobody will read the code.

Say it to your agent

“Don't write a plan yet. Build the smallest version that runs, in a scratch folder, and show me what it does on these three inputs. We will decide what the real one should do from that, and then write down what we decided.”

Whichever camp you are in, both end up building in thin pieces that run. Böckeler's objection to heavy specs is that "small, iterative steps" keep you in control, and pstack, a published set of agent instructions, orders its work the same way.

Vertical slice

Also called Tracer bullet

A small piece of a feature that goes through every layer, from what the user sees down to where the data is kept, so it works end to end even though it does very little.

An agent can write a whole layer confidently, and the mismatches only appear where layers meet, by which time a lot is resting on a guess. A slice gives it something real to run and check within the first hour.

Say it to your agent

“Build the thinnest version first: one button that saves one comment to the real database and shows it after a reload. No editing, no styling. Show me it working end to end before you add anything.”

Decomposition

Splitting a big job into pieces small enough to be done and checked alone, in an order where each piece builds on something already known to work.

Given everything at once, an agent can run out of room halfway and leave the next session a half-built feature. With small pieces, a break shows up at the piece that caused it. Too small has a cost as well, because every piece has its own overhead.

Say it to your agent

“Split this into steps that each end with something I can run. Give me the list, with the check for each step. Do step 1 only, show me the check passing, and then carry on to step 2.”

One task per session

Start a new session for each separate job, so one job's leftovers don't end up steering, or being paid for by, the next.

The whole conversation goes back on every request, wrong turns included. Three old jobs are three jobs you are still paying to re-read, and three sets of assumptions still in the room. Anything worth keeping goes in a file first.

Say it to your agent

“One job in this session: fix the login redirect. When it is merged, write what you learned to docs/progress.md. I will run /clear before the next job, so put anything I would need in that file.”

Three more ways of learning before you commit, and they are easy to mix up.

Spike

A small throwaway program written to answer one question, such as whether a library can do something or how a function really behaves. You keep the answer and delete the code.

Agents make these nearly free, so there is little reason to argue about something you could run in a minute. The usual failure is the spike's rough code quietly becoming the real code, so say at the start that it will be deleted.

Say it to your agent

“Spike this in a scratch folder, not in the repo: can SQLite enforce a case-insensitive unique email? Write the smallest script that proves it, run it, paste the output, and delete the script. No changes to our code.”

Go deep first

Get one agent doing one hard thing well, with real understanding and real checks, before you run several at once. A setup you can't trust with one agent gives you ten times the mess with ten.

Parallel agents multiply whatever you already have, and your ability to review doesn't multiply with them. The first deep run also teaches you what the checks and the brief need to say, which is what makes the later ones safe to leave alone.

Say it to your agent

“Don't fan this out yet. First do one migration end to end with me watching: find the real call sites, run the real checks, write down what went wrong. We parallelise the rest only once that one has gone through clean.”

Prototype

A quick version built to help you decide something, such as which layout or how an interaction should feel. It is meant to be thrown away, and the real one gets built properly afterwards.

Agents make these so cheap that you can see three before choosing. That speed sharpens the old danger, because a prototype looks finished. Vibe coding one is fine. Keeping it, and adding real users, is the mistake.

Say it to your agent

“Build three layouts for the settings page in a scratch folder, plain HTML, behind one switcher so I can flip between them. No tests, no framework. This is throwaway: I pick one, and then we build it properly in src/.”

One older form is worth knowing, mostly for its last clause.

User story

One short sentence about who wants something and why, usually written "As a [someone], I want [something], so that [reason]." It starts a conversation and isn't meant to be the whole spec.

For an agent, the useful part is "so that". The reason lets it make sensible choices where your brief runs out. Generated in bulk they are padding: a story that says what the code does, with no real person in it, tells nobody anything.

Say it to your agent

“As someone returning a faulty item, I want my refund to show within a minute, so that I don't contact support. Use that reason to make any choices I haven't specified, and list the choices you made.”

Two rules for once the agent is running: when to stop, and when to ask.

Stop condition

A rule the agent can check that says when to stop: these commands exit 0, or this many tries have been used. It stands in for the agent's own feeling that it has finished.

With no one watching, it needs two things: a way to succeed, and a limit for when it can't. Without the first it stops when things look done. Without the second it can spend all night on one failure. In our recordings, 1 of 40 loops never passed.

Say it to your agent

“Keep going until bun test and bun run typecheck both exit 0. Stop after 5 attempts at the same failure, and tell me what you tried, with the output. Never change a test to get there.”

Ask, don't guess

Also called Never block on the human

A rule for the agent: when the answer changes what gets built and it can't find out for itself, stop and ask instead of quietly picking. People disagree about how often that should be.

Each silent guess is a decision you never saw. Each question stops you, and stops a long run dead. Both are real costs, and what settles it is mostly how hard the guess is to undo. If it's reversible, proceed and report. If it isn't, ask.

Say it to your agent

“If a decision is reversible, pick the simplest option, carry on, and list what you picked at the end. Stop and ask me first before anything you can't undo, and before any product choice the spec doesn't settle.”

The chapter ends where this site began.

The right word

Also called Vocabulary, Say precisely what you mean

Using the exact term of the trade instead of a description: "idempotent" for "handles repeats okay", "debounce" for "waits until I stop typing". One word can carry a set of requirements you would otherwise have to spell out, if you know it.

A term points at everything a model has read about it, and you need the name more than the fix. But our worded briefs used the term and stated the rules, so we can't say how much the word alone did. And it is only useful if you can check the result, which means knowing what the word means.

Say it to your agent

“This handler must be idempotent: webhooks are delivered at least once and out of order. Dedupe by event id, and treat any event older than the newest applied to that order as stale. Use those words in the test names.”

Do it for real

Reading gets you the words. This gets you the experience behind them, and something to show.

Run our experiment on your own code

Quoting our numbers is fine. Having your own is far better, and doing it teaches you which of your sentences changed anything, which usually isn't the ones you expected.

  1. Pick one small function from a real project that deals with something awkward: money, dates, retries, or untrusted input.
  2. Before you write any brief, write 5 to 10 checks in a file the agent can't see. Include what a careful person would insist on.
  3. Write two briefs: one sentence saying what it does, and one that adds the right terms and the rules that follow from them.
  4. Run each brief 5 times in fresh sessions, in a scratch folder. Run your checks against each result and count the passes.
  5. Write down which check separated them, if any did. If none did, write that down too. That is a finding as well.

Done when: A short table of passes for each brief, plus the checks and both briefs in your repo, with a sentence saying what you can and can't conclude from so few runs.

Get interviewed, then build the first slice

This is the shape of a good agent session: questions first, the decisions in a file, then a fresh session and the thinnest thing that runs. The commits tell that story for you.

  1. Pick a feature you haven't thought through. Ask: "Interview me until you could write the spec, then write it to SPEC.md."
  2. Read SPEC.md. Add a non-goals section yourself, and at least three acceptance criteria that a program could check.
  3. Commit it. Start a fresh session and have it build one vertical slice, with a stop condition in the prompt.
  4. Open a pull request linking each criterion to the test that checks it.

Done when: A pull request with a committed SPEC.md, and a first slice whose tests map one for one to the criteria you wrote.

Decide one real thing both ways

You can only hold a view on specs against code once you have tried each on the same question. It is also a good story when someone asks where you stand.

  1. Pick a real design question you haven't settled, one with at least two sensible answers.
  2. Have one agent session write a short spec answering it, with no code. Note the time it took and the open questions left.
  3. Have a fresh session spike or prototype the same question in a scratch folder. Note the time, and what running it showed you.
  4. Write a paragraph on which told you more, and what kind of question you would use each for next time.

Done when: A short note in your repo with the spec, the spike's output and your conclusion, including anything that surprised you.

The build, stage 8 of 18

A spec before the code

A SPEC.md an agent interviewed you for and you edited by hand, and sign-in built as one thin slice with a test per criterion.

About 90 to 150 minutes, on the same app as every other stage. We ran it first and show what went wrong.

Open stage 8 of the build

Gate 8: The brief

0 of 6 right

  1. 1A webhook handler passed its tests and its reviewer's eye. In production, a refund got undone by an older "paid" event that arrived late. What would most likely have stopped this?

  2. 2An interviewer says: "You claim the right words beat a vague brief. Prove it." What is the honest answer from this experiment?

  3. 3You are alone, unsure whether a drag-and-drop board or a plain list suits your app, and nothing will ship this week. A colleague says to write a full spec first. What do you do?

  4. 4Now three agents will build against one payments service at once, starting today, and mistakes would cost real money. Which is the better first step?

  5. 5Which of these is a non-goal in the sense people mean it?

  6. 6Your agent runs for hours. It keeps stopping to ask which variable name you prefer, and last week it dropped a column without asking. What rule do you give it?

Get every answer right to install it, and to put a file in your repo. Wrong answers cost nothing.

Next chapterBackpressure

The workshop

Name a word, or describe what you mean in everyday language.