AI Coding Agent Task Design: Splitting Work Before the Agent Runs
The agent returns a 900-line diff across eleven files. Tests pass locally. The reviewer opens the pull request, scrolls twice, and still cannot say whether…

Research updated Sep 10, 2026
Key topics
The agent returns a 900-line diff across eleven files. Tests pass locally. The reviewer opens the pull request, scrolls twice, and still cannot say whether the change is correct.
That is not a model failure. It is a task design failure, and it happened before the agent ran a single token.
I have watched this pattern repeat across teams that otherwise know exactly what they are doing. They write careful prompts, they select repository context, they run the test suite. Then they hand the agent a task shaped like "add retry logic to the API client" and act surprised when the result is unreviewable. The prompt was fine. The task was not.
This article is about the layer underneath the prompt: how you split, scope, and sequence work so that the agent's output is something a human can verify in bounded time. Repository context selection is a separate problem, already covered elsewhere. Assume you can get the right files in front of the model. The question here is what you ask it to do with them.
The Diff Is Not the Deliverable

Three things get conflated in every conversation about coding agents, and separating them is the first move.
Generated code is what the agent produces. Verified behavior is what you have confirmed about the system after the change. Reviewable change is a diff a human can evaluate against a stated contract in a predictable amount of time. Only the third one ships.
The currency here is review cost. A task is well designed when the reviewer's job is bounded and predictable — when they know what to check, roughly how long it will take, and what a failure looks like. A task is badly designed when the reviewer has to re-derive the change from scratch to decide whether it is safe.
This is why "the agent wrote 900 lines and the tests pass" is not a success signal. An agent that succeeds at a task you cannot check has produced liability, not throughput. The code exists. The confidence does not.
Four levers determine review cost before the agent starts:
- Task size — how many independent claims the change makes
- Acceptance criteria — what counts as done, stated in a form that can return a binary result
- Repository boundary — what the agent is allowed to touch
- Dependency order — what must be true before this task can begin
Get these right and review becomes a checklist. Get them wrong and review becomes archaeology.
One boundary up front: this approach pays off on multi-file, multi-step work. On a one-line fix, writing a formal task contract is pure ceremony. Match the process to the blast radius.
The Contract Comes Before the Run
The four levers are easier to apply when they live in one artifact. I keep the contract compact — a handful of fields that fit on a screen:
- Claim — the single verifiable behavior this task changes
- Interface — the frozen signature or contract the change implements against
- Writable / read-only / forbidden surfaces — the declared boundary
- Acceptance checks — the binary conditions that prove the claim
- Negative criteria — what must not change
- Dependencies — what must be true before this task starts
The rest of this article explains each field once, in the order it matters. If you take nothing else from the piece, take the shape of this artifact: it is the thing you write before the agent runs, and the thing the reviewer checks against afterward.
Why Big Tasks Fail Quietly
Large tasks do not fail loudly. They fail by accumulating unverified assumptions until the composition is wrong in a way no single step reveals.
Here is the mechanism. An agent working a long-horizon task makes a locally plausible decision at step two — say, it assumes a config value is always present. At step nine, it has built three layers on top of that assumption. Nothing in the intermediate output flags the problem, because each step was reasonable given the previous one. The error is invisible not because the agent hid it, but because the agent already committed to it.
I treat this as a working model rather than a law: error compounding is a common failure pattern of long tasks, and the probability that every assumption survives is the product of many small probabilities. That product falls fast. Whether it is the dominant cause in your environment is an empirical question — the observable signals are assumption changes, a growing unrelated diff, repeated file rescans, and failed checks that appear late.
The second mechanism is context dilution. As a task grows, the agent's working set fills with its own prior output — the code it just wrote, the reasoning it just produced — rather than the repository's actual constraints. The agent starts reasoning about its own draft instead of about your system. The signal-to-noise ratio in the context window degrades, and later decisions get worse for reasons that have nothing to do with the model's capability.
The third mechanism is the one that fools people: the agent's self-report is not evidence. A confident summary of completed work is a claim. It is not a verification signal. An agent that reports "merge resolved successfully" may have deleted the conflicting file. The report describes what the agent believes it did, and belief is not observation.
There is a narrow case where large tasks work well: single-file, low-coupling changes on surfaces with a strong existing test suite. There, the test suite itself bounds the blast radius. If the change breaks something, the tests catch it, and the reviewer's job is small regardless of how much code moved. The failure mode appears when the task crosses module boundaries or touches surfaces the tests do not cover.
A practical signal to watch: before you run the agent, try to describe the expected diff in one sentence. If you cannot, the task is underspecified. You are about to discover the specification by reading the output, which is the most expensive way to write it.
Sizing a Task Around Its Verification
Most teams size tasks by line count or file count. Both are wrong, because neither predicts review cost.
Size tasks by the number of independent things a reviewer must check. That is the real unit.
This gives a clean rule: one task, one verifiable claim about system behavior. The claim should be specific enough that a reviewer can confirm or refute it without reading the whole diff. "The retry path returns 429 after three attempts" is one claim. "Improve error handling across the API layer" is five claims wearing a trench coat.
Split points that work in practice:
- By interface boundary. One task implements behind a frozen interface; another consumes it.
- By behavior. Each task changes one observable behavior.
- By layer. Persistence, business logic, and transport are separate tasks when the contracts between them are stable.
- By migration step. Schema change, backfill, cutover — each is a task with its own verification.
Split points that look reasonable and are not:
- By file. Files are not units of behavior. A change spanning three files can be one claim; a change inside one file can be four.
- By function. Functions are implementation details. Splitting here produces tasks that cannot be verified independently.
- By "frontend and backend." When the contract between them is the actual work, splitting along that line splits the work in half and leaves the hard part unowned.
When a task genuinely cannot be split, treat that as a signal rather than a constraint. Usually it means the interface is missing. Write the interface first — even as a stub — and the split becomes obvious. This is the highest-leverage move in the whole framework, and it is the one teams skip most often.
Splitting is not free. More tasks means more orchestration, more context reloading, more integration surface, and more places for the pieces to disagree. You are buying reviewability at the price of coordination. On a small, tightly coupled problem, one competent agent beats an orchestrated swarm on both quality and total time. The tradeoff only pays when the work is genuinely parallelizable.
Acceptance Criteria the Agent Can Actually Fail
A criterion is usable only if it can return a binary result without human interpretation. This is the section that most directly determines whether verification is cheap or expensive, and it is where most task specifications quietly fall apart.
"Improve error handling" is not a criterion. "The retry path returns 429 after three attempts and logs the attempt count" is. The first requires a human to decide what "improved" means. The second returns true or false.
Prefer deterministic graders where they exist. For coding work, the natural ones are: does it run, do the tests pass, does the previously failing test now pass without breaking existing ones. This is the same grading model used by established coding benchmarks — a solution passes only if it fixes the target behavior without regressing the rest. When you can express your acceptance criteria in that form, verification becomes nearly free.
But tests do not capture everything. It helps to separate the checks by owner and timing, because they are not the same kind of gate:
- Automated outcome checks — run by the harness, before a human looks. The deterministic pass/fail signal.
- Scope checks — run against the transcript and diff, also before human review. Tool call patterns, unnecessary file churn, deleted code that was not part of the task, silent scope expansion.
- Human review questions — the judgment calls that remain. Does the change compose with the rest of the system? Is this code the team will maintain?
The behaviors tests miss are exactly the ones that make review expensive. An agent can pass every test while deleting a helper it decided was unused, reformatting three unrelated files, or expanding scope into a module you did not mention. None of that fails a test suite. All of it lands on the reviewer.
Write the negative criteria too. What the agent must not change is part of the contract, not an afterthought. Untouched surfaces are a promise you are making to the reviewer, and they need to be stated explicitly or the agent will infer its own boundary.
The failure mode to watch for: criteria that only the author can evaluate. If verification requires the same person who wrote the task, you have not reduced review cost. You have relocated it. The whole point is that a different engineer — or a future version of you with no memory of the task — can check the work against the stated conditions.
Repository Boundaries and Blast Radius
Boundaries are declared, not inferred. Before the run, state three surfaces:
- Writable — files the agent may modify
- Read-only — files the agent may read for context but must not change
- Forbidden — files the agent must not touch at all
The design variable is blast radius. A change confined to one module behind a stable interface is reviewable. The same change spread across shared utilities is not, even if the total line count is identical. Shared code has more callers, more implicit contracts, and more ways to be subtly wrong.
This is why interface-first ordering matters so much. Freeze the contract, then let the agent implement against it. When the interface is fixed, the agent's job becomes local: satisfy this signature, honor this behavior. When the interface is not fixed, the agent is making architectural decisions you did not ask it to make, and those decisions are the hardest part of the diff to review.
Boundaries break down in predictable places:
- Shared configuration. Config files are touched by everything and owned by no one.
- Dependency manifests. A version bump is a cross-cutting change disguised as a one-line edit.
- Generated files. The agent may edit a generated file instead of its source, and the change will look correct until the next build.
- Cross-cutting concerns. Logging, auth, and error handling tend to leak across every boundary you draw.
Isolation is an operational requirement, not a nicety. Run parallel tasks in separate working copies or branches so they do not collide on the same files. The separation also pays a debugging dividend: when orchestration and agent behavior are separated, you always know which side broke. That distinction saves more time than the parallelism buys.
The honest limit: boundaries reduce coordination failures but do not eliminate them. Integration is still a human-owned step. The agent can satisfy every criterion inside its boundary and still produce a change that does not compose with the rest of the system. Boundaries shrink the surface where that can happen. They do not remove it.
Dependency Order and Parallelism
Build the dependency graph before assigning tasks. Nodes are tasks. Edges are "this must be true before that can start." The graph is the plan.
The critical distinction is between hard and soft dependencies. A hard dependency means the task cannot begin until the other is done — the interface must exist, the schema must be migrated, the shared type must be defined. A soft dependency means the work would be more consistent if sequenced, but it can proceed independently — style, naming conventions, logging format.
Only hard dependencies force serialization. Teams routinely serialize on soft dependencies and pay for it in wall-clock time for no correctness benefit.
Parallelism has a real cost, and it is easy to underestimate:
- Duplicated context loading across agents
- Merge conflicts when boundaries were drawn imperfectly
- Integration work that did not exist in the serial version
- Harder debugging when something breaks and you have to find which of five runs caused it
The narrow case where parallelism wins: three or more genuinely independent modules behind frozen interfaces. That is the condition. Not "the task feels big." Not "we have compute to spare." Independent modules, frozen interfaces, three or more.
The narrow case where it loses: small, tightly coupled problems. Here one competent agent beats an orchestrated swarm on both quality and total time. The coordination overhead exceeds the parallelism benefit, and the integration work eats the speed gain.
The debugging advantage of the split is worth more than the speed. When you separate orchestration from agent behavior, a failure tells you which side broke. Was it the task graph, the interface contract, or the agent's implementation? That question has a fast answer when the layers are distinct and a slow answer when they are tangled. I would trade a meaningful chunk of parallel speed for that clarity.
A Worked Task Contract
Here is the framework applied to a request teams actually write. The request is hypothetical, chosen because it is representative.
The raw request: "Add rate limiting to the public API."
Run that against an agent and you get an unreviewable diff. The agent has to decide where rate limiting lives, what algorithm to use, what the limits are, how to identify clients, what happens when the limit is hit, whether to add configuration, and whether to touch the middleware chain. Every one of those is a real decision, and none of them were specified. The reviewer now has to reverse-engineer eight decisions from the diff.
Step 1: Identify the single verifiable claim. Not "add rate limiting." Something like: "Requests from a single client exceeding N per minute receive 429 responses; requests under the limit pass through unchanged." That is one claim about observable behavior.
Step 2: Freeze the interface. Decide where the check happens and what it depends on. A middleware function with a defined signature, a client-identification strategy, and a limit source. Write the stub. Now the agent implements against a contract instead of inventing one.
Step 3: Declare the writable surface. The middleware file, its test file, and the configuration schema. Everything else is read-only. The route handlers, the auth layer, and the logging module are forbidden.
Step 4: Write acceptance criteria, including negative ones.
- Under-limit requests return their normal response.
- Over-limit requests return 429.
- The limit resets after the window.
- Existing tests pass unchanged.
- No route handler files are modified.
- No changes to the auth or logging modules.
Step 5: Order the remaining tasks. The middleware is one task. Wiring it into the request pipeline is a second, dependent on the first. Configuration loading is a third, independent of both if the config interface is frozen.
The resulting specification is compact: scope, interface, criteria, forbidden changes, dependencies. It fits on a screen.
What the reviewer's job becomes: a bounded checklist. Does the middleware match the frozen signature? Do the four criteria hold? Were the forbidden files touched? That is a review measured in minutes, not a re-derivation measured in hours.
The friction points, honestly: freezing the interface took longer than writing the original request. Deciding client identification was a real design conversation, not a formality. And the negative criteria were the hardest to write, because they required thinking about what the agent might reasonably do that we did not want. That thinking is the work. The agent just executes it.
Where Task Design Stops Helping
Credibility comes from naming the boundaries of your own advice, so here they are.
Exploratory work resists decomposition. When you do not yet know the shape of the solution, a tight contract is premature. You cannot freeze an interface you have not designed. Exploration is a legitimate phase; it just is not the phase where task contracts help. Explore first, then contract.
Legacy code with no tests and unclear interfaces. The first task is usually to build the verification surface, not to change behavior. Adding a characterization test around the code you are about to modify is a task with a clear claim and a clear boundary. Changing behavior in untested legacy code is not.
Small changes. The overhead of a formal contract exceeds the review cost it saves. A one-line fix does not need a specification document. Match the ceremony to the risk.
Judgment-call work. API design, naming, architecture — these cannot be reduced to binary criteria and should not pretend to be. You can contract the mechanical parts around them, but the judgment itself stays human.
The residual risk that task design does not remove: the agent can satisfy every criterion and still produce code your team will not maintain. Criteria bound correctness, not taste. A change can be verifiably correct and still be something you would not have written. That gap is real, and no amount of specification closes it. It is the reason review does not disappear — it just gets cheaper and more focused.
What to Practice Next
Start with one real task from your backlog. Write the contract before running the agent: the single claim, the frozen interface, the writable surface, the criteria including negatives, the dependencies. Then compare the review cost against your last unplanned run. The comparison will teach you more than this article can.
Build a small reusable template — scope, interface, criteria, forbidden changes, dependencies — and treat it as a team asset rather than a personal note. The second time you run a similar task, the contract is already written and the review checklist is already known. That is the compounding argument: a task contract is a reusable artifact, and reusable artifacts get stronger with every run. The payoff is not automatic — it depends on teams actually maintaining the templates and checks — but the mechanism is real.
Practice writing negative criteria. Most teams write what should change and forget what must not. The negative criteria are where review cost actually lives.
Practice interface-first decomposition on a task you would normally hand over whole. Freeze the contract, then split. Watch how much of the ambiguity disappears.
Two adjacent skills worth building: designing deterministic checks before reaching for model-based grading, and reading diffs as evidence rather than as output. Both make task design pay off faster.
Here is the decision rule. Before you run the agent, write two sentences: the change you expect, and the check that will prove it. If you cannot write both, the task is not ready. No model upgrade fixes a task you have not defined.
The teams that get durable value from coding agents will not be the ones with the best prompts. They will be the ones whose task contracts, interfaces, and verification surfaces are reusable assets — assets that make the next run cheaper than the last.


