The failure mode with coding agents is not wrong code. Wrong code shows up in tests and review. The failure mode is a 900-line diff that nobody reads properly, merged because it looked plausible and the tests were green.
Every rule below exists to keep the diff small enough that reading it is realistic.
The loop
- Write the task down in one sentence, plus the acceptance check.
- Create a branch or worktree for it.
- Run one agent session, scoped to that task only.
- Run the tests and the typechecker yourself.
- Read
git diffline by line. - Commit, open a pull request, end the session.
Six steps, one commit, one review. Repeat rather than expand.
Set the boundary before the agent starts
An agent working directly in your main checkout will stage files, stash things and leave your working tree in a state you have to untangle. Give it its own copy.
git worktree add ../app-rate-limit -b feat/rate-limit
cd ../app-rate-limitWorktrees share the object database but have independent indexes and working directories, so two sessions can run at once without colliding. When you are done:
git worktree remove ../app-rate-limitThen constrain what the session can run. Deny network commands, deny
git push, deny reads of .env. The point is not that the agent is malicious
— it is that a wrong command executed confidently costs you an hour.
Write the task as an acceptance check, not a paragraph
The habit that changes results most: state the task as something that is mechanically true or false.
Weak prompt:
Add rate limiting to the API.
Strong prompt:
Add a per-IP rate limiter to
src/middleware/rate-limit.ts. 100 requests per minute, sliding window, backed by the existing Redis client insrc/lib/redis.ts. Return 429 with aRetry-Afterheader. Makesrc/middleware/__tests__/rate-limit.test.tspass — I have already written it and it currently fails.
The second version names the file, names the dependency, states the contract, and hands over a failing test. It removes every decision the agent would otherwise make silently and you would have to reverse-engineer from the diff.
Writing the test yourself takes ten minutes and it is the ten minutes that makes the review possible.
Keep sessions to one commit’s worth of work
Session quality degrades with length, for a mundane reason: the context fills with earlier attempts, reverted decisions and tool output, and later edits are made against a muddier picture than the first ones were.
Practical rules:
- One task per session. End the session at the commit, do not continue into the next task.
- Clear rather than compact between unrelated tasks. Compaction keeps a lossy summary, which is how an agent “remembers” a decision you already reverted.
- If the agent has tried the same fix twice, stop. The third attempt is not going to be different. Read the code yourself, find what it is missing, and restart with that fact in the prompt.
Review the diff, not the summary
The summary an agent writes describes its intent. The diff describes what happened. These diverge quietly and the divergence is exactly what review is for.
git diff --stat
git diff
git diff -- '*test*' # read test changes separately, and carefullyRead the test changes on their own pass. An agent asked to make a suite pass has two routes available, and weakening an assertion is the cheaper one.
Three questions per diff:
- Does anything here touch a file the task did not mention?
- Did any test get weaker — a removed assertion, a widened type, a new
skip? - Is there a new dependency? Check what it is and who publishes it.
Where agents reliably fail
Being specific about this is more useful than a general caution.
Cross-cutting refactors. Renaming a concept across forty files produces a diff you cannot meaningfully review, and partial application is worse than none. Use your editor’s rename refactor.
Anything with a security boundary. Authentication, authorisation, session handling, payment flows, permission checks. Not because output is worse there, but because the cost of a subtle error is unbounded and review is hardest.
Database migrations. Generate the migration with your ORM’s tooling, and
read every line. An agent-written ALTER TABLE on a table with production data
is not worth the time it saves.
Code you do not understand. If you cannot review it, you cannot merge it. That constraint is on you, not on the tool.
Dependency choices. Agents suggest packages that were popular in the training data. Check the repository, the last release date and the maintainer count yourself.
What to track
Two numbers tell you whether this is working:
Diff size per merged commit. If it is climbing, sessions are getting too long and review is getting shallower.
Revert rate. Commits reverted within a week, split by agent-assisted and not. If agent-assisted commits revert more often, the scoping is wrong, not the tool.
Both are one git log away and neither needs a dashboard.
Common failure modes and the fix
The agent edits files outside the task scope. The prompt named a behaviour rather than a location. Name the file.
Tests pass locally, fail in CI. The agent ran a filtered subset. Put the exact CI command in your project instructions and require it before the agent reports done.
The change works but reads nothing like the surrounding code. Your project
conventions are not written down anywhere the agent can read them. That is a
missing CLAUDE.md section, not a model problem.
Two sessions conflict on the same files. They shared a checkout. Use worktrees.
Review takes longer than writing it would have. That is real, and it is the correct signal to do the task yourself. Agents pay off on well-specified, mechanical, test-backed work. They do not pay off on the parts of the job where deciding what to build is the hard bit.