DEV Community

pickuma
pickuma

Posted on • Originally published at pickuma.com

Using Claude Code Subagents for Parallel Refactoring: A Hands-On Workflow

A single agent refactoring a 40-file module works the way you'd expect: it reads file one, edits it, reads file two, edits it, and so on, in a straight line. The bottleneck is sequential context-building. Every file it touches has to pass through one context window, one at a time. If each file takes a couple of minutes of read-reason-edit, a wide rename or interface change turns into a long, linear crawl that you babysit.

Subagents change the shape of that work. Instead of one agent walking the tree, you dispatch several, each owning a slice of it, each with its own context window. The orchestrating agent holds the plan; the subagents do the edits. When the slices don't overlap, they run at the same time. This is less about raw speed and more about parallelism where the work is genuinely independent — and that distinction is the whole game.

When parallelism actually helps

Not every refactor splits cleanly. The deciding question is whether your slices share state. Two subagents editing the same file will clobber each other's edits, because each one read the file before the other wrote to it. The orchestrator can't reconcile two divergent versions of auth.ts — one of them silently wins.

So the workflow starts with partitioning, not dispatching. Good candidates for parallel slices look like this:

  • By directory. src/components/, src/lib/, and src/pages/ rarely share files. One subagent per top-level folder is a safe default.
  • By concern that maps to distinct files. "Update all the test files" and "update the source files" touch disjoint sets.
  • By mechanical pattern. Renaming an import across the codebase, where every edit is the same shape, parallelizes well because the per-file reasoning is shallow.

Bad candidates share a hot file. A change to a central type that every module imports means every subagent wants to read and reason about that one type — and several may want to edit the file that defines it. That's sequential work wearing a parallel costume.

Before you dispatch anything, run a quick conflict check: list the files each slice will touch and look for any file that appears in two slices. If one does, pull it out and edit it yourself first, in the orchestrator, before fanning out. Subagents cannot see each other's in-flight edits, so an overlap is a guaranteed lost write, not a maybe.

The five-step loop we run

We've settled on a loop that keeps the orchestrator in control and the subagents narrow. The point of narrow subagents is that a small, well-scoped task is one you can actually verify when it comes back.

1. Survey first, in the main agent. Have the orchestrator map the change before touching code: which files match, what the dependency edges look like, where the shared types live. This survey is what you partition against. Skipping it is how you end up with overlapping slices.

2. Write the partition down. Produce an explicit list — slice name, files, the exact instruction. Treat any file that two slices both want as a flag to resolve now, not later. This list is also your review checklist when the work returns.

3. Edit shared files in the orchestrator. Anything central — a type definition, a config, a barrel export that every slice imports — gets edited once, by the main agent, before fan-out. Now the subagents read an already-correct shared surface and only touch their own files.

4. Dispatch one subagent per slice. Each gets a self-contained instruction: the files it owns, the change to make, and the success check ("the module typechecks", "these tests pass"). A subagent that has to ask a clarifying question mid-run was under-specified in step two.

5. Merge and verify in one place. When slices return, the orchestrator runs the build and the full test suite against the combined result — not per-slice. A slice can pass in isolation and still break an integration point another slice changed. The only verification that counts is the one over the merged tree.

Keep slice instructions blunt and bounded. "In src/components, rename the variant prop to tone on every component that defines it, and update the prop type — do not touch files outside src/components" is a good slice. "Modernize the components" is not; it invites scope creep that collides with neighboring slices.

What this costs and where it breaks

Parallel subagents spend more tokens than one linear pass, because each subagent re-establishes its own context. You're trading tokens for wall-clock time and for the orchestrator's context staying clean — it never has to hold all 40 files at once, only the plan and the slice boundaries. On a wide, mechanical change, that trade is usually worth it. On a deep, interconnected one, it isn't, and you're better off with a single agent that can hold the whole dependency chain in one head.

The failure mode to watch is silent partial completion. A subagent might finish its slice, report success, and still have missed a file the survey didn't catch — a dynamic import, a string-built path, a file outside the directories you partitioned. The merged-tree build catches most of these; a grep for the old symbol across the whole repo catches the rest. Trust the verification step, not the subagents' self-reports.

If you'd rather drive this from an editor with the diff in front of you instead of a terminal transcript, an agent-aware IDE makes the merge-and-review step less abstract — you watch each slice land as a reviewable change set.

The honest summary: subagents are a partitioning tool, not a speed button. The value comes from how cleanly you cut the work, not from how many agents you launch. Spend the effort up front drawing slice boundaries that don't touch, edit the shared surface yourself, and verify once over the whole. Do that and parallel refactoring is calm. Skip the partition and it's a race condition you're running by hand.


Originally published at pickuma.com. Subscribe to the RSS or follow @pickuma.bsky.social for new reviews.

Top comments (0)