Available for rolesPiotr Czerwiński

Writing · July 30, 2026 · 9 min read

Running coding agents in parallel with git worktrees

Claude Code · AI agents · git · workflow

TL;DR: One giant agent session building a whole feature drifts: context fills with half-finished threads, the model loses the plot, and you spend the back half of the session re-explaining the front half. The alternative I settled on is an orchestrator pattern. One session plans and splits a feature into three to five parallel, low-conflict subtasks with the contracts between them agreed up front. Each subtask runs in its own git worktree - isolated, with pushing physically disabled and no database access - and a final integration session merges the branches into one working feature with an end-to-end pass. The whole thing lives or dies on the quality of the split.

The loop

The shape is fan-out then fan-in, and it repeats feature by feature.

Split. The orchestrator session, with my approval, breaks the feature into N parallel subtasks and defines the seams between them before any code is written. This is the step that matters, and I will come back to it, because everything downstream is either easy or painful depending on how well it is done.

Fan-out. Each subtask goes to its own isolated session on its own worktree, running to completion without stopping to ask. They run in parallel. None of them can push, and none of them can reach a real database.

Fan-in. When all subtasks are done, a dedicated integration session merges the branches into one working feature: wiring the seams together, gluing the UI, and running the end-to-end checks after I have started the apps by hand.

Repeat. Merge one wave completely before you dispatch the next. Overlapping waves is how you rebuild the merge hell you were trying to avoid.

The split is the actual skill

Everything good about this pattern comes from disjoint work with clear contracts, and everything bad comes from overlap. Two subtasks editing the same files is merge hell; two subtasks writing the same schema is a guaranteed conflict. So before dispatching anything, the orchestrator draws a conflict map: which repository, which files, and what is shared - the translation strings, the schema, the common types.

Three principles keep the map clean. Define the contract up front. The seam is what lets independently-built pieces meet later: a dependent piece builds against the agreed contract plus a mock, and the real wiring happens only at integration - the admin UI builds against the shape of an endpoint that does not exist yet, and they are introduced in the fan-in. A shared resource gets exactly one owner. The database schema, the shared types - one subtask owns them, and everyone else builds against the contract. Never two subtasks writing the same schema. Three to five pieces is the sweet spot. Past that, merge debt and the cognitive load of holding the whole thing in your head eat the gains. If a feature wants eight pieces, it wants to be two waves of four.

The payoff of doing the split well is that integration becomes light. Light integration is not something you achieve by being clever at merge time; it is something you buy by minimizing overlap at split time. The work moves earlier, to where it is cheap.

Isolation that is a guarantee, not a promise

Parallel agents editing code is only safe if they genuinely cannot step on each other or on anything real. Git worktrees give each session its own checkout of the repo on its own branch, which is the foundation:

# each subtask gets its own checkout on its own branch
git -C <repo> worktree add ../feat-x -b feat/x develop
cd ../feat-x && npm install   # node_modules not shared

# make push physically impossible in this worktree
git -C ../feat-x remote set-url --push origin DISABLED-no-push

On top of that, three properties turn isolation from a hope into a guarantee. Push is physically disabled by pointing the push URL at a dead address, so an attempt to push simply fails; it goes back on only after a merge is approved. The environment file is gitignored, so it never lands in the worktree - which means the agent has no credentials and no way to touch any database. That is a hard guarantee from the filesystem, not a behavioral promise from the model. And a separate directory means a separate build cache, so a parallel session does not stomp on the dev server running against your main checkout.

The gates that never move

Automation runs inside fixed rails. Applying a schema migration, merging to the mainline branch, pushing, and deploying to production are mine, regardless of how the automation is configured. The integration session merges locally, does not push, and does not apply migrations - when a migration is needed, it prints the command for me to run from my own machine. The worst case this design allows is a branch I never merge, and that is exactly the worst case you want.

Why it beats one big session

Two reasons, and they compound. First, throughput: several capable agents building genuinely independent pieces at once is a level of parallelism a solo builder never had, and on a subscription plan it costs nothing extra to run them together. Second, and less obvious, each session stays lean. A focused session on one subtask keeps a clean, high-signal context, which is where these models do their best work - as opposed to one sprawling session whose context slowly fills with noise until quality quietly degrades. The orchestrator pattern is not just faster; it keeps every participant, including me, working on one clearly-bounded thing at a time.

What carries over

  • Split for disjointness, not for balance. Low file and resource overlap is what makes parallel work merge cleanly; equal size is irrelevant.
  • Agree the contracts before dispatch. Dependent pieces build against a contract and a mock; real wiring waits for integration.
  • One owner per shared resource. Two sessions writing the same schema is a conflict you scheduled in advance.
  • Make isolation physical. Disable push, keep secrets out of the worktree; a guarantee from the filesystem beats a promise from the model.
  • Keep the irreversible gates human. Migrations, mainline merges, and deploys stay yours; the worst case becomes a branch you never merge.
  • Merge a wave before starting the next. Overlapping waves rebuilds the merge hell the pattern exists to avoid.