2026-09-04 · git · githubactions · design · claudecode
What Kills Parallel Development Is Always Shared Files
Has this ever happened to you when trying to run multiple tasks in parallel?
- You worked on three features on separate branches, and all of them conflicted in the same file.
- After resolving and merging, another branch conflicted in the same spot again.
- In the end, you ended up running them one by one.
The cause is usually a shared file. And this can be avoided by how you split the work.
I’ve encountered this repeatedly when using AI to implement features in parallel, so I’ll outline patterns and countermeasures. This isn’t limited to AI; the same issues occur in human teams.
The Culprits Are Usually Predictable
The files that actually conflicted were almost always one of these five types:
- Routing definitions —
routes/index.ts,<Route>lists inApp.tsx - Type aggregation files — bundles of re-exports like
types/index.ts - DI container entry points — places where dependencies are registered in one spot
package.json— overlapping dependency additions- Migration indexes — ordered lists
They share a common trait: they are lists. A structure that adds one line every time a new feature is added will inevitably cause multiple people (or agents) to edit the same location simultaneously.
Worse yet, conflicts here are semantically harmless. Person A adds one line, Person B adds another. Keeping both results in the correct state. Yet Git cannot resolve them automatically, halting human progress.
Countermeasure 1: Tackle Shared Files First
This is the most effective approach. Isolate the task involving shared files and run it first.
Issue #10 Add three entries to route definitions Scope: src/routes/index.ts
Issue #11 Implement settings page Scope: src/pages/settings/** Depends on #10
Issue #12 Implement billing page Scope: src/pages/billing/** Depends on #10
Issue #13 Implement notifications page Scope: src/pages/notifications/** Depends on #10
#10 only requires "adding routes to screens that don’t exist yet," so it finishes in minutes.
After that, #11, #12, and #13 do not touch any shared files, allowing them to run completely in parallel.
This won’t work if the order is reversed. If you implement #11 while also adding routes,
all three will compete for the same file.
"Is it weird to add routes to non-existent screens?"
You might worry that this temporarily breaks the build. In reality, it passes if you leave placeholder components in place.
// src/pages/settings/index.tsx ← #11 implements the actual content
export default function Settings() {
return null;
}
Place empty boxes first. That alone enables parallelization.
Countermeasure 2: Adopt Structures Without Lists
If you can eliminate "list files" altogether, that is more fundamental.
- Routing → Automatic collection from directory conventions (file-based routing)
- Type aggregation → Don’t create
types/index.ts; import directly from each module - Migrations → Determine order via timestamps in filenames, without an index file
It’s worth isolating this as a single task. Once done, future parallelization becomes much easier.
However, doing this abruptly in an existing project has a wide impact scope, so it’s good to have a criterion like "change the structure after three conflicts."
Countermeasure 3: Decide Not to Touch What You Can’t Touch
When having AI implement features, this is effective. Explicitly define the scope of responsibility, and stop and report if the scope is exceeded.
- Do not modify paths outside the Scope written in your assigned task
- If changes outside the Scope are required, stop implementation and report
If changes expand silently, information indicating that the split was wrong disappears. If it stops, you can fix the split there.
Here is a real example. During one task, a report came from the implementer:
tsc --noEmitcannot resolve types fornode:pathbecause@types/nodeis missing indevDependencies. I did not modifypackage.jsonas it is out of Scope. Please decide whether to isolate this as a separate task.
This is a deficiency in the foundation, not due to that specific task.
If package.json had been modified without permission, unrelated changes would have mixed into that PR,
blurring the review criteria. It was isolated as a separate task and fixed via an independent PR.
Countermeasure 4: Merge Serially
Even if splitting is correct, parallel merging ruins it.
Merging three PRs simultaneously can cause failures after all three are merged, even though each passed tests individually. And you cannot determine which one is the cause. You end up re-examining all three, making it slower than serial merging.
If you merge one by one, each subsequent PR is evaluated against the state of main at that time.
If it breaks, you can confirm the previous single item was the cause.
Implementation in parallel, merging in serial. That is the practical conclusion.
Conflicts Are Symptoms, Not Causes
When conflicts occur repeatedly, improving how you resolve them is meaningless. Your splitting method is wrong.
The frequency of conflicts can serve as a metric for the quality of your split.
| Frequency | What It Indicates |
|---|---|
| Rarely happens | Splitting is effective |
| Happens occasionally | Unexpected dependencies exist. Resolving them per occurrence is sufficient |
| Happens every time | Multiple tasks are competing for shared files. Re-evaluate the split |
If it happens every time and you resolve it ad hoc, symptoms disappear but the cause remains, and costs continue to rise.
Summary
- Parallel development is usually stopped by shared files with a "list" structure
- Run the task touching shared files first. Placing empty boxes first is sufficient
- If possible, adopt structures without lists (automatic collection based on conventions) for a more fundamental fix
- Stop and report if you go outside your assigned scope. Silent expansion hides split errors
- Implementation in parallel, merging in serial
- Conflict frequency measures split quality. If it happens every time, suspect the split
I have documented this splitting approach as a configuration for Claude Code’s sub-agents and published it under the MIT license.
I publish the configuration for splitting Claude Code into separate personas —
Architect, Coder, Reviewer, Conflict Resolver — under MIT. Copy it, run
./setup.sh, and it works. It does not depend on your tech stack.
https://github.com/quintetkit/quartet
I built one real tool using nothing but this workflow. Every Issue, PR, review and merge is still there. The parts that went wrong were not deleted.
https://github.com/quintetkit/mdlinkcheck
The version that adds a UI Designer persona, review criteria, a per-Issue parallel execution script and a 10-chapter guide is on the product page.
The workflow itself is available
Quartet, the four-persona version, is published free under MIT. Quintet adds a UI Designer persona, review criteria, a per-Issue parallel execution script, and a 10-chapter guide.
See the free version Product page