Notebook

The half of Brooks's law that survives agents

Running many agents in parallel is the real unlock for a small team; what limits it is contention over shared state, which draws the same curve as Brooks's law by an entirely different mechanism.

Two stacked panels. Above, four agent tracks converge into a single shared lane where a brass burst marks a collision and the lane continues to main as a broken dashed line. Below, the same four tracks each run in their own isolated lane and rejoin at one clean brass merge point before reaching main.PARALLEL AGENTSCONTENTION VS ISOLATIONSHAREDCOLLISIONMAINONE WORKING COPYISOLATEDMAINCLEAN MERGEONE WORKING COPY EACH

A failure class we have hit, written as a class because the instances are interchangeable: two sessions work in one repository on unrelated features. One finishes and commits the ordinary way. Stage the file, write the message, push. The commit carries part of the other session's unfinished work, because the file it staged was one that session had edited a minute earlier. Nothing errors, the types check, the deploy goes green. Half-written work sits in production looking finished, because none of those checks ask whether a file was done.

The second class involves no source code at all. Giving every parallel stream its own isolated workspace closes the first class, and we will get there. It also gives each stream its own copy of the dependency tree. Run enough of them on one machine and the disk fills. Every stream on that machine then fails at once, on unrelated work, for a reason none of them can see from inside its own context. In both cases, several streams reach the same limited resource and go down together.

What Brooks's law predicts

Both classes look like the oldest result in software management. Brooks's law says adding people to a late project makes it later, and it is usually quoted without the mechanism, which is the part that matters. Brooks gave two. The new person has to be trained by someone already producing, and communication paths grow roughly with the square of the headcount, so each addition brings more channels than hands. Capacity rises in a straight line and coordination cost rises faster, so past the crossing point another person costs more than they return. That curve is the half that transfers.

The mechanism is the half that fails, because both of Brooks's are missing. Briefing an agent costs a file read and some marginal context, so the training dip is gone. Agents carry no communication overhead with each other, because they do not communicate at all. There are no pairs and no channels, so the square term has nowhere to come from. The cost that climbs faster than the work comes from somewhere else, and the two failure classes say where. The streams share state, and each stream added raises the chance that two of them touch the same file, row, port, or disk. Every one of those touches can destroy work that neither stream can see.

The distinction matters because the two mechanisms have opposite remedies. Brooks's remedy is a smaller team with less surface between its members. The remedy for contention is a harder boundary around each stream, not a smaller number of them. The bill also lands differently. Human coordination cost is visible in advance, in meeting time and review latency. Agent coordination cost stays invisible until it has already happened, and it gets paid in rework. Someone reads a diff line by line to work out which half of it belongs to another stream's unfinished feature, and then undoes that half.

The unlock is genuine

The hazard matters only because the upside is real. Our work used to run serially for one reason: one person holds one problem in their head at a time. The migration waited on the feature, the copy pass on the layout, the flaky test on everything. We had been treating that ordering as a fact about the software when it was a fact about how many people we had. Run several streams at once and most of the queue dissolves, which buys wall-clock time today. That advantage erodes as the tooling reaches everyone.

Two stacked panels. Above, four agent tracks converge into a single shared lane where a brass burst marks a collision and the lane continues to main as a broken dashed line. Below, the same four tracks each run in their own isolated lane and rejoin at one clean brass merge point before reaching main.PARALLEL AGENTSCONTENTION VS ISOLATIONSHAREDCOLLISIONMAINONE WORKING COPYISOLATEDMAINCLEAN MERGEONE WORKING COPY EACH
Parallel streams against one repository. What makes a small team fast here also eats the gains back when nothing keeps the streams apart.

Agents collide differently than people do

Two humans editing the same file is a solved social problem. They know the other one exists. Someone mentions they are in that file today, or notices the branch, or asks. They also work slowly enough that the window for a collision is narrow, and they tend to check before overwriting.

Two agents editing the same file each produce a change that is complete, self-consistent, confidently explained, and incompatible with the other. Neither hedges, and neither knows the other is running. A merge conflict is the lucky case, because it is the outcome the tooling notices. The worse case is two edits that apply cleanly and mean opposite things, which is how a feature gets implemented twice. File conflicts are still the easier half to catch. The contention that costs most is over resources no version-control system has an opinion about:

  • The same port, where two dev servers on one machine each assume they own it and one ends up serving the other's build.
  • The same database row, when two streams fix one record from different directions and each write is the last one.
  • The same disk and memory, which are finite on any shared machine and get reached sooner than anyone plans for.

Isolation, ownership, queues

Isolation first, because it closes the first class outright. Every stream gets its own workspace on disk: a checkout per branch, with its own working tree and index. Two streams cannot interleave inside one file mid-write, and neither can stage the other's changes, because there is no shared index to stage into. Separate working trees prevent that commit even when nobody remembers to check.

Ownership second, which closes less than it appears to. One owner per area at a time, and a cheap way to see whether somebody already has it: check when that area was last written to. A commit from the last half hour is reason enough to ask.

the cheapest check we have
git log -1 --format='%ci %s' -- path/to/area
# recent write? assume someone owns this right now; coordinate or go elsewhere

That check is a convention rather than a lock, and it fails in the situations that need it most. It cannot see uncommitted work, which is most of the work worth worrying about, and two streams starting in the same minute both read a clean history and proceed. Skipping it costs nothing until the collision. One command is still worth spending, as long as it is called a convention. A real lock would make every stream claim the area somewhere that all of them have to read first.

Queues third, for the resources that stay shared. Batch work gets enqueued and handed to workers one claimed row at a time, so the shared resource faces one writer while the others wait. The jobs wait their turn instead of all reaching the edge of the disk at once, which is the answer to the second class.

The per-branch workspace is private memory: it stops one stream's half-written file from leaving inside another stream's commit. The ownership rule is an attempt at a single writer, so that two incompatible implementations of the same requirement do not both end up looking finished. The queue serializes the contended resource, which keeps every stream on a machine from finding the edge of the disk at the same moment.

Five producers on the left feed one queue lane. The lane holds four occupied slots followed by four empty dashed slots waiting, then passes through a single brass worker gate that opens onto a shared resource on the right.JOB QUEUECONTENTION, SERIALISEDPRODUCERSQUEUEWAITINGWORKERONE AT A TIMESHARED RESOURCEONE WRITER
Shared resources stop being a race and become a line, one claimed unit at a time. That is slower than letting the streams contend, and it does not take the machine down with it.

What limits the organization

The obvious limits on an agent-heavy team are budget and model quality. Both are real, and in our experience the coordination structure binds earlier than either. Two studios with the same models and the same bill can get different amounts of work out of the same number of streams, depending on how much state those streams are allowed to share.

Writing the feature is the part that scales with spend now. The structure that keeps ten streams out of each other's way still gets built by hand, out of artifacts checked into the repository: a workspace layout, a rule about who owns which area today, a job queue with a claim step, a note on what the shared machine can carry. We revise each of them the first time it fails.

More from the notebook