← Back to BlogSpec-Driven Dev

How to Write a Spec an AI Coding Agent Can Follow

A practical guide to writing specs AI coding agents can execute: goal, inputs and outputs, constraints, edge cases, explicit scope, and acceptance criteria.

When an agent finishes a task from a one-line prompt, the only way to judge the result is to look at what it built and decide whether that seems reasonable. That is review by consequence: read the diff, run it, and pattern-match against what you would have written yourself. It works fine for small, unambiguous changes. It falls apart the moment a task has more than one reasonable interpretation, because the agent picked one of them for you, silently, and you are left grading a choice you never saw made.

A specification reverses that order. Instead of inferring intent from the output, you state intent first, in writing, then check the output against it. A spec that names the goal, the inputs and outputs, the constraints, the edge cases, what is explicitly out of scope, and the acceptance criteria turns review into a checklist instead of a guess. This matters more for an agent than it ever did for a new hire, because an agent has no shared history to fall back on when the prompt runs out. It will fill the gap with something, and that something is rarely what you meant.

Review by Consequence vs. Review by Intent

This is the reasoning behind treating a spec audit as a gate before implementation starts, rather than a review step added after the fact. Catching a missing constraint in a paragraph costs a few minutes of editing. Catching the same gap in a four-hundred-line diff costs a rewrite, and possibly an incident if nobody catches it at all. The cost of ambiguity does not disappear when the spec gets skipped. It moves later, and it gets more expensive on the way.

Writing it down also solves a problem that used to be handled by shared memory. A human collaborator carries yesterday's conversation about scope into today's work. An agent's context resets between sessions, and even inside one long session earlier reasoning can get compacted or dropped as the context window fills. The spec is what survives that reset, in the same way a persistent, private memory layer like MemX is what lets an AI carry context across separate conversations instead of starting cold each time. Put the decision in the document, not only in the conversation, and it survives the next context window.

The gap between a vague prompt and a precise spec is not only clarity, it is measurable reliability. METR's benchmark of frontier coding agents found close to 100% success on tasks that take a skilled human under four minutes, and under 10% success on tasks that take more than roughly four hours, with the length of task an agent can complete at 50% reliability doubling about every seven months across six years of model releases. A vague prompt for anything beyond a small change is effectively several long, ambiguous tasks stapled together. A spec that states scope and edge cases up front breaks that into the kind of short, well-bounded task agents are already reliable at.

Sources: METR: Measuring AI Ability to Complete Long Tasks

What a Precise Spec Contains

A spec an agent can actually execute against answers six questions. Leave any of them unanswered and the agent has to guess, and guesses are exactly what turn up as review work later. This is the same premise behind spec-driven development: treat the specification as the artifact everything gets checked against, not a formality written to satisfy a process.

This lines up with how Anthropic's own Claude Code documentation frames it: the most useful specs are self-contained, name the files and interfaces involved, state what is explicitly out of scope, and end with a verification step that proves the feature actually works. That is close to the same list, reached independently from watching engineers use an agent day to day.

Sources: Claude Code: Best practices

  • Goal: the single outcome the change produces, stated as one sentence the agent could repeat back correctly.
  • Inputs and outputs: exact file paths, function signatures, and data shapes, including where they come from and where they go.
  • Constraints: the non-functional requirements the agent cannot infer from the code, such as a performance budget, a disallowed dependency, or a security rule.
  • Edge cases: the specific inputs and states already known to happen, and what should happen for each one.
  • Out of scope: what the change explicitly does not touch, so the agent does not expand the task on its own.
  • Acceptance criteria: the checkable conditions that make the task done, given as test cases or commands, not a description of a feeling.
Pro Tip

Before implementation starts, ask the agent to restate the spec in its own words and flag anything ambiguous. A mismatch caught at that point costs a sentence of clarification. The same mismatch caught after the code exists costs a rewrite.

Vague Prompt vs. Precise Spec

The difference is easiest to see side by side, dimension by dimension.

ElementVague promptPrecise spec
GoalImplied by a one-line requestStated as one outcome the agent can restate back correctly
Inputs and outputsInferred from surrounding codeNamed explicitly: file paths, signatures, data shapes
Edge casesDiscovered mid-implementation, if at allEnumerated up front, with expected behavior for each
Scope boundaryLeft to the agent to decideStated as what the change explicitly does not do
Review basisDoes the diff look plausibleDoes the diff satisfy each acceptance criterion

Constraints and Edge Cases

Constraints are the requirements an agent has no way to infer from the code alone: a performance budget, a rule against adding a new dependency, a security rule such as never logging a credential, or a style convention that contradicts the file sitting next to the one being changed. State these explicitly. An agent asked to add caching has no way to know it needs to survive a restart without serving stale data unless the spec says so.

Edge cases work the same way. List the specific inputs and states already known to occur: an empty list, a duplicate submission, a request that arrives after a token has expired, a write that lands in the middle of a migration. An agent that hits an edge case nobody named picks a behavior for it anyway, and that behavior ships as part of the system whether anyone intended it or not.

A Worked Example

Take a request to add rate limiting to a login endpoint. A vague prompt (add rate limiting to /login) leaves the agent to decide the limit, the time window, the storage backend, and what a blocked request gets back. Each of those is a real decision, and the agent makes all of them without telling anyone.

A spec for the same task states the goal as one sentence: reject login attempts beyond five per minute per account, without blocking other accounts attempted from the same IP. It names inputs and outputs: the existing Redis client as the counter store, a 429 response with a Retry-After header for blocked attempts. Constraints: no new dependency, and the check has to run before password verification so it also throttles credential-stuffing attempts made with valid emails. Edge cases: what happens when Redis is unreachable (fail open and log it, rather than locking out every user), what happens to the counter on a successful login (reset it), and what happens across a horizontally scaled deployment (the counter has to be shared, not kept per instance). Out of scope: no admin interface for unblocking accounts, no change to the signup endpoint. Acceptance criteria: a test that a sixth rapid attempt on one account returns 429, a test that attempts against five different accounts from one IP all succeed, and a test that a Redis outage degrades to allowing requests instead of blocking everyone.

None of that changes what the feature is. It changes what is reviewable. Once a spec like this exists, an adversarial pass, the kind a convergence gate runs before anything ships, can check the diff against those five behaviors directly instead of relitigating what add rate limiting was supposed to mean in the first place.

Insight

A spec does not make an agent's code better by making the agent smarter. It makes the code reviewable, because a reviewer can check the output against stated intent instead of reverse-engineering intent from the output after the fact.

Frequently asked questions

01How long should a spec be?

Long enough to answer the six questions (goal, inputs and outputs, constraints, edge cases, out of scope, acceptance criteria) and no longer. A spec for a rate limiter is a few paragraphs. A spec for a new service is a document. Padding it with background the agent can already get from reading the code just adds noise it has to filter out.

02Who writes the spec, the engineer or the agent?

Usually both. State the goal and constraints directly, since only the engineer knows why the change matters and what is off-limits. Let the agent draft the inputs, outputs, and edge cases from the codebase, then correct it. Reviewing a drafted spec is faster than writing one from a blank page, and it surfaces gaps in the original thinking before any code exists.

03What about a genuinely exploratory task, with no acceptance criteria yet?

State that explicitly instead of skipping the section. Write down what the task is trying to learn and how you will know when you have learned it, even if that is 'produces three viable approaches to compare' rather than a pass or fail test. An exploratory task with no stated success condition just becomes an unreviewable one.

04Does a detailed spec slow the agent down?

It slows down the first few minutes, since writing constraints and edge cases takes longer than typing one sentence. It speeds up everything after that, because there is less back-and-forth correcting misread intent, and less time spent reviewing a diff that has to be reverse-engineered for meaning.

Ship AI-written code you can trust

TLM Forge is the missing process layer for Claude Code: a spec audit, independent multi-agent review, enforced TDD, and an adversarial red-team gate.

Get TLM Forge