Writing Software With Coding Agents All Day
10 min read · updated August 4, 2026
This codebase — an LLM gateway with a ledger, six provider adapters, a thousand-page content library and two hundred interactive tools — was written almost entirely with coding agents, including about forty running in parallel across two waves. The pattern in what worked and what did not is sharper than expected, and it is not about the model.
What this is an account of
Two distinct modes, and they behave completely differently.
- Fan-out. Forty agents, one file each, working from a shared plan document they all read and a brief they were all given. Zero merge conflicts across two waves. This mode is astonishingly effective and almost all of its risk is in the coordination, not in the code.
- Depth. One agent on one hard problem — a billing invariant, a bundle-splitting refactor, a security lane. This mode is where the wrong turns happen, and where the value is highest when it goes right.
Where they saved days
- Breadth with a real specification. The plan document named, per row, a slug, one primary keyword, an intent and an angle. Eight hundred pages came back with zero duplicate keywords from writers who could not see each other’s output. That worked because the source document was authoritative and they were told to copy from it exactly, not because they coordinated.
- Reading code they did not write. Consistently the strongest use. Several of the most valuable findings in this repository came from an agent asked to audit a lane and reporting a missing check that six sibling paths carried and the seventh did not. That comparison is tedious for a human and trivial to ask for.
- Finding errors in the plan itself. The content plan was unusually careful and still wrong in six places: four keywords used on two rows each, breaking its own uniqueness rule; one slug identical to a cluster id, which would have made that page permanently unreachable; and a release order burying the row the plan itself calls the classic backlink magnet behind forty calculators and five months. Five of the six were found by agents writing against it, not by the person who wrote it — an agent doing one cluster reads its twenty rows far more closely than anyone reads all thousand.
- Reading the test suite as documentation. One agent noticed that the suite discovered cluster files by a filename pattern that silently skipped a file named with a suffix, and therefore skipped twenty finished pages. Nothing failed. Nothing was missing from any index. An agent reading the test found it.
- Writing the differential test. Checking a hash implementation against published standard vectors, a hand-written regular-expression engine against the platform’s own across hundreds of combinations, a diff algorithm against a brute-force reference on 500 cases. Tedious to specify by hand, cheap to ask for, and it caught four real algorithmic bugs.
Where they cost a day
Three failure shapes, each of which cost real time here.
Confident work at the wrong layer
The bundle-splitting refactor took two wrong turns before the right one, and both were plausible. The first attempt at a metadata generator parsed the source files for an exported array, which produced 130 of 140 entries — one file built its array from a map expression rather than a literal, which is fine code and invisible to a pattern. A generator that silently drops ten pages is worse than no generator.
The second was subtler and typechecked, built and shipped: a lazy import placed inside a server component’s render. It split nothing, because the lazy boundary has to exist in the client graph. The route stayed at 441 kB of first-load JavaScript, and nothing anywhere reported a problem. The final version reached 112 kB.
The lesson is that the failure mode is not nonsense; it is competent work aimed one layer away from the problem. Nothing catches that except measuring the thing you actually wanted to change.
Reporting gaps that were not gaps
One session opened with a claim that four capabilities were missing from the chat surface and identified the biggest as stop-generation. Two of the four already existed, fully built: per-model abort controllers, the send button becoming stop, an escape-key binding, and abort distinguished from a real failure. The retry path existed too, complete with an in-file comment describing the exact fix that was about to be made again for the same reason.
That comment is the interesting part. The information needed to avoid the wasted work was sitting in the file, written by a previous session, and was not read. A related file carried a long comment about a CSS layering trap; the same trap was hit again one rule above it.
Repeating a trap the codebase had already documented
This is the generalisation of the previous point and the single biggest recurring cost. Long explanatory comments in this repository exist precisely because something was expensive to learn, and the same class of mistake recurred anyway when a later session did not read them. The practical mitigation was to make the comments impossible to miss: put the warning at the top of the file rather than beside the line, and state the consequence rather than the rule.
The four instructions that changed outcomes
- “Read this file first.” One finished, good example beats a style guide, and it is cheaper to produce. Every agent in the second wave was pointed at one reference file before its own brief.
- “Create exactly this file. Create, edit or delete nothing else.” Disjoint files rather than disjoint instructions. Shared registries are wired afterwards by the orchestrator, because agents that edit a shared file collide.
- “Other agents are writing sibling files right now — if a typecheck reports an error in a file that is not yours, ignore it.” Without this line they try to repair a half-written neighbour, and two agents editing one file is the only way this arrangement breaks.
- “Never write ‘we measured’. Record every promise you could not keep, with the row number and the reason.” The recording requirement is what made it real. An agent that must write down which promise it broke and why does not quietly fabricate instead — roughly 120 angles were rewritten and every one is documented in its file’s header.
The fourth generalises well beyond content. Asking for an explicit, written account of what could not be done is the highest-value instruction on this list, and it costs one sentence.
When twelve of twenty were cut off mid-flight
One wave lost twelve of twenty agents part-way through their files to a session limit. The recovery that worked, in order:
- Typecheck to find what is actually broken. Only one file was. Most partial files were syntactically complete and simply short.
- Delete the genuinely empty ones rather than committing scaffolding somebody would later mistake for a starting point.
- Keep the ones holding definitions others depend on, with an empty collection and a comment naming exactly which rows are missing and stating that the finished components above are unreferenced until somebody writes their entries.
- Say plainly in the commit which rows never got written, so the tracker reports 140 of 200 rather than a number nobody can reconcile.
Partial output is the normal case at this scale, not an exception, and having a rehearsed procedure for it is worth more than trying to prevent it.
They will decline things, if you ask
Given an explicit instruction that fabrication was the worst available outcome, agents declined work rather than inventing it. One refused to build a live-litigation tracker on the grounds that it would be wrong within weeks and unmaintainable. One wrote on its own page that it would give no current status for GPU supply, because any status it gave would be false within months. One quoted no water-usage figure at all, because every published figure is a division of two estimates.
None of that judgement appears unless you ask for it explicitly. The default behaviour is to produce something plausible, and plausible is the failure that costs the most, because it looks like success.
The rule that emerged
| Rule | Description |
|---|---|
| Verify at the layer you cared about | A refactor that typechecks, builds and ships can still have changed nothing. If the goal was a number, measure the number. |
| The specification carries the coordination | Uniqueness across forty agents came from one authoritative column in one document, not from anything the agents did. |
| Ask for the failures in writing | 'Record what you could not do and why' is the cheapest instruction here and produced the most value. |
| Put the hard-won knowledge where it will be read | At the top of the file, phrased as a consequence. A comment beside the line gets missed, and the trap gets hit twice. |
| Plan for partial output | Typecheck, delete empties, keep depended-on definitions, state what is missing. Rehearse it before you need it. |
| Review depth work harder than breadth work | Fan-out failures are visible and local. Depth failures typecheck. |
The broader practice questions — what to hand over, how to review it, what to keep doing yourself — are in working with coding assistants and reviewing AI-written code.