Writing Requirements an AI Coding Agent Can Build From / Aug 21, 2026

9 min readBy Emmanuel Akinfulubi
Writing Requirements an AI Coding Agent Can Build From

Here is a feature request I have written myself, in almost exactly these words: "When someone pays, send them a receipt."

The code that came back was fine. It charged the card, it sent the email, it looked correct in review. Three days later a customer had been charged with no receipt, because the email provider had a bad afternoon and the agent had decided, on my behalf and without mentioning it, that a failed send should be logged and swallowed.

That was the right call, arguably. It was also a decision I never made, alongside two others I did not notice: the webhook was not idempotent, so a retried payment notification sent a second receipt, and a customer who paid twice by accident saw nothing at all to tell them the first payment had already gone through.

One sentence of intent. Three decisions I did not know existed.

The agent is not the problem

This is the part worth sitting with. The agent behaved correctly given what it was told. It was told to send a receipt after payment, and it sent a receipt after payment. Everything else was a gap, and something had to fill it.

Vague requirements were survivable when a human read them. A human reads "send them a receipt", pictures the email provider going down, and walks over to ask you what should happen. That conversation is the mechanism that used to catch this, and it worked because ambiguity was expensive to resolve alone.

An agent does not ask. It decides, instantly and plausibly, and it does not tell you it decided. The gap gets filled either way, just without you in the loop.

That is the whole shift. Coding agents made writing code cheap and moved the hard part upstream, and this is what upstream looks like in practice. Not more documentation. Fewer gaps.

A vague request on the left, the decisions an agent makes silently in the middle, and the acceptance criteria that close each gap on the right.

Every gap gets filled. The only question is whether you filled it or the agent did.

The test for a requirement

There is one test, and it is quick.

Could someone write a passing or failing check from this sentence without asking you a question?

If they would have to come and ask, the sentence is intent, not a requirement. Intent is useful. It just is not buildable, and handing intent to an agent is handing it a decision.

Three before and after pairs, all of them sentences I have actually written:

"The system should be fast." Nobody can check that. Try instead: the system shall return search results within 400 ms at the 95th percentile, for a catalog of up to 50,000 items. Now there is a number, a percentile and a scale, and someone can prove you wrong.

"Handle invalid input gracefully." Whose definition of graceful? Try: if a submitted date falls outside the booking window, then the system shall reject the submission, keep every other field as entered, and state the earliest and latest dates that would be accepted.

"Users can upload a profile photo." This one looks complete, which is what makes it dangerous. It says nothing about size limits, accepted formats, what happens to the previous photo, whether upload is atomic, or what a shopper sees while it is in flight. Five gaps in six words.

EARS, and why the keywords help

EARS is a small set of sentence patterns for requirements. There are five, and the value is not the notation. The value is that each pattern forces a question you were avoiding.

PatternShapeUse it for
UbiquitousTHE SYSTEM SHALLAlways true, no trigger
EventWHEN trigger, THE SYSTEM SHALLSomething happens
StateWHILE in state, THE SYSTEM SHALLBehavior that holds during a condition
OptionalWHERE feature exists, THE SYSTEM SHALLApplies only to some configurations
UnwantedIF condition, THEN THE SYSTEM SHALLFailure and edge handling

Take them one at a time, on the receipt feature.

WHEN forces you to name the trigger. "WHEN a payment is confirmed" is a different requirement from "WHEN a payment is initiated", and the gap between them is where the receipt for a failed card comes from. Writing the word makes you pick.

WHILE forces you to think about duration. "WHILE a payment is pending, the system shall show the order as awaiting confirmation and shall not send a receipt." Without it, pending is an undesigned state, and an undesigned state is one the agent invents.

WHERE forces you to be honest about configuration. "WHERE a customer has opted out of marketing email, the system shall still send transactional receipts." That distinction is both a product decision and a legal one, and it is exactly the kind of thing that gets discovered after launch.

IF-THEN forces you to admit the thing can fail. This is the pattern that would have saved me. "IF the receipt fails to send, THEN the system shall retry three times with backoff, and SHALL flag the order for manual follow-up if all attempts fail." I did not write that sentence, so someone else decided it.

SHALL, on its own, forces you to commit. Not should, not could, not ideally. A requirement written with "should" is a preference, and a preference is a gap wearing a disguise.

You will not keep the capital letters forever, and that is fine. Keep the questions.

The requirements everyone skips

If you only take one section from this, take this one. Six questions, asked of every feature that touches anything you cannot recreate:

  1. What happens on retry? The user pressed the button twice, or the network did.
  2. What happens on partial success? Two things had to happen and only one did.
  3. What happens on concurrency? Two requests arrive for the same record at once.
  4. What happens when a dependency is down? Not slow. Down.
  5. What happens when the same request arrives twice? Which is retry, from the other side.
  6. What does the user see in each of the above?

The receipt bug was numbers two and five. Payment succeeded, email failed, and nothing in the system knew those were supposed to be one outcome.

Money makes the cost of omission unarguable, so use it as your worked case even when the feature is not about money. Consider "let customers apply a discount code at checkout". A reasonable sounding sentence with an expensive number of gaps: expired codes, codes already redeemed, two codes at once, whether the discount applies before or after shipping and tax, case sensitivity, and where rounding happens. Get the last one wrong and you have a floating point currency bug in an orders table, which is not a thing you fix later. It is a thing you reconcile by hand, for months.

An agent will resolve every one of those in about a second, and will be silent about all of them.

What changes in the output

Honest version, because this is not magic.

Fewer invented decisions, because there is less left to invent. A diff you can review against a list instead of against your memory of a conversation, which matters more as review becomes the actual bottleneck. Tests with something to assert, because a testable criterion is most of a test case already. And a real answer to "why does it do that", which is the question you cannot answer about code you did not write and did not specify.

What it does not do: it does not make the agent correct. It makes the agent's mistakes visible. Those are different claims, and only the second one is true.

One feature, fully specified

The discount code, taken from a sentence to something buildable.

WHEN a shopper submits a discount code
  THE SYSTEM SHALL apply it to the order subtotal only,
  excluding shipping and tax.

THE SYSTEM SHALL match codes case-insensitively and ignore
  surrounding whitespace.

THE SYSTEM SHALL accept at most one code per order.
  WHEN a second code is submitted, THE SYSTEM SHALL ask the
  shopper to confirm replacing the applied code.

IF a code has expired
  THEN THE SYSTEM SHALL reject it and show the date it expired.

IF a code has already been redeemed on that account
  THEN THE SYSTEM SHALL reject it and say it has been used.

THE SYSTEM SHALL calculate the discount in integer minor units
  and SHALL round at most once, at the order total.

Six criteria. Ten minutes to write. Every one of them is a test, and the last one is the difference between a clean ledger and a reconciliation spreadsheet.

Notice what is not in there: no implementation, no table names, no library choices. Requirements say what must be true. How it gets done is the next document, and conflating the two is how requirements turn into an unreadable design doc that nobody updates.

The habit

Write the criteria before the prompt, not after the diff.

That is the entire practice. Not a template, not a process, not a document nobody reads. Ten minutes of deciding what must be true, ahead of an agent that will otherwise decide it for you and never mention that it did.

You will still get things wrong. You will get them wrong on purpose, in writing, where you can find them later, which is the only kind of wrong worth having in a codebase you may need to explain months from now.

I am writing more on this, the parts that come after requirements: design that constrains rather than decorates, and sizing work so an agent can finish it. If that is useful, the box below will let you know when the next one lands.

Frequently asked questions

Is this overkill for a small feature?
For a one line change, yes. The threshold is whether anyone will need to understand the decision later. If the behavior touches money, identity, data you cannot recreate, or anything a second person will maintain, write the criteria. If you are renaming a variable, do not.
Do I have to use the exact EARS keywords?
No. The keywords are scaffolding for the questions they force. WHEN makes you name the trigger, IF-THEN makes you admit the thing can fail. Once asking those questions is a habit, you can drop the words and keep the rigor.
Does writing requirements slow me down?
At the start, yes, by ten or fifteen minutes per feature. It removes more time than that from review and rework, because you stop discovering decisions in a diff that you should have made before the diff existed.
Can the agent write the requirements for me?
It can draft them, and drafting is genuinely useful for surfacing cases you missed. It cannot be the thing that decides the trade-offs. Whether an expired discount code fails loudly or silently is a product decision you are accountable for, and handing it to the agent is how you end up with behavior nobody chose.