DEV Community

quintetkit
quintetkit

Posted on

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 definitionsroutes/index.ts, <Route> lists in App.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
Enter fullscreen mode Exit fullscreen mode

#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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 --noEmit cannot resolve types for node:path because
@types/node is missing in devDependencies. I did not modify package.json as 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.

Top comments (0)