You have a small shopping list app, and it needs two things: a filter for the items and a CSV export of the list. The two tasks barely touch each other, and the idea suggests itself: split the work in two and get both parts in half an hour instead of an hour. One worker takes the filter, the other takes the export, and each gets their own working folder so the edits don't get mixed up. Nobody discussed up front how the two parts would interpret a list item. Seemed obvious.

Half an hour later both parts are done. Each part's own checks are green, and the merge went through without a single conflict. But the end-to-end scenario doesn't work: on screen the filter shows three rows, and in the exported file the column is empty. The cause is boring. One of the workers renamed the completed field to done along the way, because it read better, and the other one didn't know about it.

Separate folders kept the files apart, but they didn't align the decisions. Let's work through this in three steps: first decide whether this work is worth splitting at all, then divide up the areas of change and the shared agreements, and finally check the assembled result, so a surprise like this doesn't make it all the way to the end.

Check the dependencies

There are usually two reasons to split work. The first is independent research into different areas: finding out at the same time how the list is displayed and what builds the export. The second is working in parallel on parts whose boundaries and verifiable result are already known. A layout like "planning here, execution here, review here" doesn't need three participants by itself; one session does it all in turn. For a small task it makes sense to start with a single worker.

If there is a reason, the next question is this: what part of B's work can go ahead while A's result is still unknown? If B depends entirely on A, those steps run one after another. Sometimes B can prepare an independent part in advance, but combining the results still has to wait for A.

Here's how that plays out on the same shopping list:

What needs doingCan start at the same timeWhy
Find where the list is filtered now, and find what builds the exportyeseach one reads a different part of the code, and the findings don't depend on each other
Prepare the final fix for a cause that's still unknownafter the cause is confirmedresearch comes first; different testable hypotheses can be investigated in parallel
Write the filter and write the exportafter agreeing on a shared item formatboth parts rely on the same field; without a shared decision the results will diverge

The third row is the most common trap. Since the filter lives in src/filter.js and the export in src/export-csv.js, it seems the parts are independent: the files are different, after all. But both read the same item and the same completed field. That's exactly how the scenario from the start of the article broke, and separate files didn't save it. Independence is about behavior and shared agreements, not about how many files the code sits in.

You can hand the breakdown itself to the agent, just without launching any workers:

Split the task into parts. For each one, list the inputs, the result,
the dependencies and how to verify it. Mark which parts can start now.
Don't launch any workers yet.

You can check an answer like this before launching anything. Parts with no open dependencies are candidates for starting in parallel. An empty dependencies column is worth checking against the project: the agent may have missed a shared model or setting.

Count the time to a shared result

Speedup is usually measured with a single number: how long the work itself took. What you should count is the time to an accepted shared result. Besides the work itself, that includes writing up the task for each worker, each of them reading the project again, waiting, checking two results and assembling them into one.

A rough calculation for our example. The numbers are just there for the arithmetic, not a performance measurement:

What we countOne worker, one part after anotherTwo workers in parallel
Extra prep for the split: two task write-ups and coordination between the workers0 minutes5 minutes
Execution20 + 20 minutes20 minutes for both parts
Checking and assembling the results10 minutes15 minutes
Total to an accepted result50 minutes40 minutes
Total if one part had to be redone in 15 minutes because the item was understood differently50 minutes55 minutes

The general task prep is needed in both cases, so it isn't in the calculation. One redo and the ten-minute gain is gone. And the more expensive the assembly, the more time parallel work has to save.

Besides time, there are two more costs that are easy to forget. Each extra worker reads the project from scratch, which costs resources, and detailed summaries take up room in the main conversation once they come back. The second cost matters more: your own attention. Two results have to be read, compared against each other and decided on. If you can't keep up with reading what comes in, the speed isn't yours anymore.

A nearly twofold speedup is possible for two comparable independent parts with small overhead. But the number of workers alone doesn't promise it.

Duplicate or cross-check

Duplication doesn't show up in reports. Two workers quietly open the same files, both figure out how a list item is structured, and each one honestly writes that they studied the code. But sometimes a second pass is needed on purpose: an assumption is critical, and you want it checked by someone who hasn't seen the first one's reasoning. What sets such a cross-check apart from an accidental duplicate is that its subject, its reason and what you'll do if the answers diverge are named in advance. Without that last part, the check turns into an opinion poll.

Now the opposite situation. Two workers were asked what completed means in a list item. Both gave the same answer: a mark that the item has been bought. Looks like confirmation from two sides.

In fact, both read the same line in README.md, written a year ago, while in the code the field has long meant something else. What matched wasn't their conclusion, it was their source.

Which leads to a couple of working habits:

  • ask to be shown what the answer is based on: a file, a line, a code snippet. Not a retelling, but something you can open yourself;
  • check a single shared assumption separately and for real: what the code does and what happens in the scenario. Even when everyone agrees.

Separate sessions don't guarantee independent mistakes: a shared source gives a shared miss. And when the answers diverge, look at the evidence instead of counting votes: one worker may be looking at the current code and two at an outdated note.

The field's meaning, verified against the code, becomes the basis of the shared model that gets agreed on before the start. If the split still looks worth it after all three checks, move on to dividing things up.

Assign an owner to each area

The rule for the duration of parallel work is simple: one area of change has one owner. Not "two people must never touch this file", but "while these two tasks are running, this person is the one who writes here". An agreement for a period of time, not a property of the project.

The second half of the rule matters more than the first, and it gets forgotten more often. The shared result has an owner too: the person who puts the parts together and says whether the whole thing is done. If nobody is assigned to the assembly, it falls to whoever finished last, and that's exactly the person who knows least about the other half.

Before launching, look at the project and check that the parts you need are already separated in some way. If they aren't, that's no reason to start a big refactoring for the sake of parallelism: it would itself become the biggest task of the day, and there'd be nothing left to split.

For our shopping list, the split looks like this:

AreaWho changes itCondition
src/filter.js, tests/filter.test.jsWorker ASelects items by the agreed completed field
src/export-csv.js, tests/export-csv.test.jsWorker BExports the set of items it's given in the agreed format
src/item-model.jsIntegration ownerThe shared item model is agreed before the start; changes are discussed with everyone who depends on it
src/app.js, tests/filter-export.test.jsIntegration ownerWires up both parts and checks the export of the filtered list
package.json, package-lock.jsonIntegration ownerWorkers don't add new dependencies along the way

The table assigns responsibility for changes, not access rights: shared files are read by everyone who needs them.

The package.json row isn't there out of spite. npm install changes both files at once, so two parallel installs create a conflict out of nowhere, and two different versions of the same library bring a surprise at build time. If both workers need a library, they send back a request saying what's needed and why. One person makes the decision and the edit, once.

Agree on a shared model before the start

File boundaries work poorly without a shared agreement. The filter and the export rely on the same item model. Below is a sketch of their interfaces, not a runnable example: the function implementations aren't shown here. Before the start, everyone also agreed to keep the order of items, not to modify the original array, and to write boolean values to CSV as true/false. The CSV escaping rules stay as they were.

// one list item: these three fields are agreed before the start and don't get renamed
const item = { id: 41, title: "oat milk", completed: false };

// filter.js returns a ready set of items, it doesn't format or export anything
filterItems(items, mode);   // mode: "all", "active", "completed"

// export-csv.js takes an already filtered set and doesn't select anything itself
exportCsv(items);           // returns a CSV string with the columns id,title,completed

Notice that the agreement here isn't about file names, it's about input and output. "The export receives an already filtered list" sounds like a detail, but that one line decides which of the two is responsible for selecting items. Without it, both could reasonably assume the selection is on their side, and then the file gets either everything or items filtered twice.

Before the start you also agree on how the result will be accepted. The end-to-end scenario for our example: items 41 and 43 have completed: true, item 42 has false. The "completed" mode should show 41 and 43 in their original order, and the CSV should contain the same two items with the header id,title,completed and the value true in the last column. The scenario goes through both parts at once and catches exactly the bug this article started with.

One more thing, about changing the agreement itself. If it turns out along the way that the shared format has to change, that's not a small edit to one file. It's a reason to revisit every task that depends on the format, because some of the work already done may have relied on the old version.

Separate working folders (git worktree) don't replace the agreement. They keep the edits physically apart, and that's useful: in a shared folder a check can pick up unfinished changes from the neighboring task and confirm a state that no single worker has actually checked. But a folder can't agree on the item format, and ports, local databases and other external resources stay shared. An untracked .env isn't in the new folder at all by default: you copy it over, for example with .worktreeinclude.

Give each worker a short brief

A brief doesn't have to be long. Seven points are usually enough: the goal, which version to start from, the worker's own files, the shared agreements, a verifiable result, what to return in the reply and what to do if someone else's area is needed.

Goal: the list filter shows all items, only incomplete ones or only completed ones.
Start from: the latest commit on the main branch where the item model is agreed.
Your files: src/filter.js and tests/filter.test.js.
Shared agreement: the completed field stays boolean, field names don't change;
the filter returns an array of items and doesn't export anything.
Result: the filter tests pass; on a set of three items each mode
gives the expected list.
Return: the list of files you actually changed, the test run output
and the version you ran them on, any open questions.
If you need to change src/item-model.js or src/app.js: stop,
describe the change you need and why, and wait for a reply.

The brief for the second worker is built exactly the same way: its own pair of files, the same item format, its own verifiable result and the same steps for crossing a boundary. No need to write it out in full here.

Now for the reply. It needs the actual list of changed files and the actual output of the checks. The phrase "other people's files were not touched" is easy to write, and the harder you asked for it, the more readily it will show up in the reply (funny how that works). Check against the list of changes, not the wording. The boundary in a brief works as an agreement, not as a ban: the text of the request doesn't stop the agent from writing to a file, and access permissions are configured separately.

If a subagent does part of the work

In Claude Code a subagent runs in its own context window. A regular subagent doesn't see your conversation and works from the task description Claude writes for it; a fork inherits the whole conversation. So it's better to name the shared agreements and criteria explicitly in the brief. What comes back is a summary: it's easy to read, but it doesn't show what exactly changed in the files, and it takes up room in the main conversation itself. More in the subagents documentation.

Renegotiate the overlap

Sooner or later someone runs into another worker's boundary. Worker B finds out that without an edit to src/app.js there's nowhere to plug the export in at all: the export button never gets called.

A good reply contains the reason, the change needed and the dependency. Wiring in src/app.js is already assigned to the integration owner, so B can finish their module and pass on a request for the wiring. If B's part can't continue without the shared change, the dependent work is paused.

From here there are two moves. The integration owner makes the change to the shared file themselves, since it's their area. Or they explicitly hand the area over to worker B, but first make sure the previous owner isn't writing to that file right now: a handover without a stop creates exactly the situation the split is meant to prevent.

What you definitely shouldn't do is make the worker work around the boundary. A request like "don't touch app.js, figure something out" usually produces a copy of someone else's logic in the worker's own file, or an extra layer nobody can explain later. On paper the table is respected. in reality the project now has a second version of the truth about how the export works.

Assemble behavior, not just files

Before assembly, the owner needs to know three things: which version each part started from, what each worker checked and on which version, and which files actually changed. All of this should already be in the replies to the briefs, and the file list gets checked against the actual changes.

Then the parts are merged, and now comes the part that actually matters, running the end-to-end scenario agreed on before the start. Two very different things can get in the way.

A text conflict. The parts changed the same section in different ways, deleted or renamed a file, and Git couldn't merge them automatically. You resolve the conflict and then check the resulting behavior.

A semantic incompatibility. Nobody touched the same line, the text merges cleanly, and there's no conflict message. And this is exactly where our rename case lives: the filter reads done, the export reads completed and gets nothing. Meanwhile the checks for both parts are green, each one checking its own thing on its own data.

A clean merge doesn't mean compatibility

A merge without conflicts doesn't prove that the behavior is compatible. You can run the end-to-end scenario through the UI or with a test that ties the functions together; local checks don't replace it.

Two parts of the work come together in one assembly and are checked by an end-to-end scenarioWorker AWorker BIntegration ownerEnd-to-end scenariofilter and export togethermerges the partssrc/filter.jssrc/export-csv.js
One owner assembles the parts, and only the end-to-end scenario tells you whether they're compatible

In the diagram, look at the bottom arrow. The upper half is what people usually call splitting the work: two independent parts in their own files. But the conclusion that it's done lives one level down, in the scenario that goes through both parts at once. Until the scenario at that arrow passes, you have two local results and no shared one. If the project's rules require the full set of checks, the end-to-end scenario doesn't replace it.

Record the shared decision

After assembly, the owner of the shared result picks the next step. For example:

  1. Accept the shared result: the scenario passes, the changes make sense, the criteria are met.
  2. Send a specific part back for rework, naming exactly what doesn't match and with what. Not "the export is kind of weird", but "the export reads a field that's no longer in the model".
  3. Rethink the split itself: reduce the number of workers or make the parts sequential.

The third option is normal, not a failure. The signs that it's time to rethink the setup show up fairly quickly:

  • the shared item format keeps changing, and each time both parts get redone after it;
  • the workers wait for each other, which means there was a dependency, just an unnamed one;
  • results come in faster than you can read them.

The last sign is the nastiest, because from the outside it looks like success. And one successful split isn't a pattern yet: an agent's results vary from run to run, so an important conclusion is worth repeating on a comparable task.

A partial result is a normal outcome too, by the way. The filter can be done and checked while the export isn't, and that's worth writing down as it is: one part out of two. The overall task stays open, though. You can't count it as closed on the grounds that half of it works, and no amount of careful splitting changes that.

Check yourself

Two situations. Answer on your own first, then compare.

The cause of the bug hasn't been found yet.
The export sometimes produces an empty file, and it's tempting to hand the fix to two workers right away. Start by reproducing and investigating. If distinct testable hypotheses come up, you can hand them to two workers. But two proposed fixes without evidence don't solve the task yet: each one has to be tied to the cause and checked against the original scenario.

Different files, the same number.
The filter counts the quantity of an item in units, and the export writes the same quantity in packs. The checks for both parts are green, but the numbers on screen and in the file don't match. This isn't a bug in the code: neither part broke its boundary. What's missing is an agreement on the unit of measure. It gets recorded in the shared item model before the start, the integration owner makes the edit in src/item-model.js, and then the end-to-end scenario is run again.

What Claude Code takes on here

The agent will propose a draft breakdown, as shown at the start of the article, and turn your points into a coherent brief. Three more places where it's handy, though the call is still yours:

  • A draft map of areas. Ask it to go through the project and propose which files change for which part of the task, with paths. It's only a draft: you assign the owner of each area.
  • Assembly and the end-to-end run. The agent will merge the parts, show the full list of changes and run the end-to-end scenario. After that, review the changes yourself, all of them.
  • A disputed spot. Two confident explanations don't make a decision final. Ask for evidence, a file and a line; who changes a shared file is settled in advance by the table, not by whose answer sounds more convincing.

In the end, it all rests on one decision and two assignments. The decision is whether to split at all: each extra worker has to bring a benefit that justifies preparing, checking and assembling their work. The assignments are an owner for each area of change and an owner for the shared result. That's exactly what the shopping list story was missing: neither an agreement on the completed field nor someone to run the filter and the export together. Tools take the mechanical part of the work off your hands, but the conclusion that the assembled behavior really is what was ordered stays with a human.