Skip to content

Parallel Execution at Scale

Boris has dozens of Claudes running at all times — and this is how he does it.

Running a single Claude instance is like having one junior developer. Running dozens is like having a team — each working independently on a separate branch, none blocking the others, all reporting back when done.

The enabling technology is not special infrastructure. It is git worktrees.

The Core Concept: Git Worktrees

A git worktree is a separate checkout of your repository in a different directory. Each worktree has its own working tree and index, but shares the same git object store. Two worktrees can be on different branches without affecting each other.

When Claude runs in a worktree, it operates on an isolated branch. It can create files, run tests, commit changes — and none of it touches your main working copy or any other worktree.

Terminal window
# Standard setup — one directory, one branch
~/myproject/ # branch: main
# Worktree setup — multiple directories, multiple branches
~/myproject/ # branch: main
~/myproject-auth/ # branch: feat/auth ← Claude instance 1
~/myproject-billing/ # branch: feat/billing ← Claude instance 2
~/myproject-search/ # branch: feat/search ← Claude instance 3

Each Claude instance sees only its own branch. There are no file conflicts. No merge headaches mid-session. When a Claude finishes, it opens a PR, and you review it independently of the others.

Setting Up Worktrees

CLI: the -w flag

Terminal window
# Start Claude in a new worktree automatically
claude -w
# Claude creates a new worktree + branch, opens session there
# Branch name is auto-generated from the task description
# With an explicit label
claude -w --label "feat-auth"
# Creates branch: feat-auth-{timestamp}
# Worktree: ../myproject-feat-auth/

Desktop App

In the Claude Code Desktop app, check “New Worktree” when starting a session. The app handles directory creation and branch naming automatically.

Manual setup (for full control)

Terminal window
# Create a worktree manually
git worktree add ../myproject-auth -b feat/auth
# Start Claude in that directory
cd ../myproject-auth
claude
# List active worktrees
git worktree list
# Remove a worktree after merging
git worktree remove ../myproject-auth

The Mental Model

Think of each Claude instance as a junior developer with their own branch. You are the tech lead.

Your job:

  • Identify which tasks are independent (no shared files, no ordering dependency)
  • Assign each task to a Claude instance with a clear brief
  • Check in when notified of completion
  • Review the PR, request changes if needed, merge when green

Claude’s job:

  • Complete the assigned task in isolation
  • Write tests, run them, iterate until passing
  • Open a PR with a clear description
  • Wait for review

You do not coordinate between Claude instances. They do not know about each other. Isolation is the design.

The Dozens-of-Claudes Workflow

Step 1: Decompose into independent tasks

The prerequisite for parallelization is task independence. If task B requires output from task A, they cannot run in parallel.

Independent (parallel-safe):
✓ Add email notifications → touches: email/, templates/
✓ Add CSV export feature → touches: export/, api/export.ts
✓ Fix pagination bug → touches: components/Pagination.tsx
✓ Add dark mode → touches: styles/, theme/
✓ Write API documentation → touches: docs/
Dependent (must be sequential):
✗ Add database migration → then → Add model layer
✗ Add auth middleware → then → Protect existing routes

Step 2: Start each Claude with -w

Terminal window
# Terminal 1
claude -w --label "feat-email-notifications"
# Terminal 2
claude -w --label "feat-csv-export"
# Terminal 3
claude -w --label "fix-pagination"
# Terminal 4
claude -w --label "feat-dark-mode"

Give each Claude its task brief immediately after starting.

Step 3: Label and track

Use the --label flag so you can identify sessions at a glance. In the Desktop app, session names appear in the sidebar.

Terminal window
# See all running Claude sessions
claude sessions list
# Attach to a specific labeled session
claude sessions attach feat-csv-export

Step 4: Let them run

Walk away. Boris checks in on parallel sessions the way a tech lead checks on a team: periodically, not constantly. Claude will surface blockers if it encounters them.

If a session gets stuck (missing context, unclear requirements, needs a decision), you will see it in the session output. Clarify and let it continue.

Step 5: Review PRs as they complete

As each Claude finishes, it opens a PR. You review them in whatever order makes sense — small ones first, or by priority.

Terminal window
# Check which PRs are open
gh pr list
# Review a specific PR
gh pr view 143 --web

Step 6: Squash merge

Each PR gets a squash merge. Clean commit on main. The worktree is removed.

Terminal window
gh pr merge 143 --squash --delete-branch

/batch for Structured Fan-Out

For highly structured parallel work — where you have a list of tasks that all follow the same pattern — use /batch instead of manually starting each session.

/batch process these 12 API endpoints and add OpenAPI documentation to each

/batch handles session creation, task distribution, and result collection. See /advanced/batch for the full reference.

Example: Migrating 200 Files to a New API

A real use case where parallel execution is the only practical approach.

Scenario: Your codebase has 200 files that use the old fetchUser(id) API. The new API is userService.get(id). You need to migrate all of them.

Sequential approach: One Claude processes all 200 files. Takes ~2 hours, all in one giant PR, high risk if something goes wrong mid-migration.

Parallel approach:

Terminal window
# Split files into 10 groups of 20
ls src/ | split -l 20 - group-
# Start 10 Claude instances, one per group
for i in $(seq 1 10); do
claude -w --label "migrate-batch-$i" &
done

Brief for each instance:

Migrate the following 20 files from fetchUser(id) to userService.get(id).
Files to migrate:
- src/components/UserProfile.tsx
- src/components/UserAvatar.tsx
[... 18 more files ...]
Rules:
1. Import userService from '@/services/user' if not already imported
2. Replace all calls: fetchUser(x) → await userService.get(x)
3. If the calling function is not async, make it async
4. Run `npm test -- --testPathPattern={filename}` after each file change
5. Do not modify any files outside this list
Open a PR when all 20 files are done and tests pass.

10 PRs land over the next 30 minutes. You review and merge them in sequence. Total elapsed time: ~45 minutes. One Claude would have taken 2 hours, and you would have been blocked on that branch the entire time.

The Parallel vs. Sequential Comparison

graph TD subgraph SEQ["Sequential (Single Agent)"] S1[Task A] --> S2[Task B] --> S3[Task C] --> S4[Task D] S4 --> S5[All Done: 4x time] end subgraph PAR["Parallel (Multi-Agent)"] P0[Decompose Tasks] --> P1[Claude 1: Task A] P0 --> P2[Claude 2: Task B] P0 --> P3[Claude 3: Task C] P0 --> P4[Claude 4: Task D] P1 --> P5[Review & Merge] P2 --> P5 P3 --> P5 P4 --> P5 P5 --> P6[All Done: 1x time] end style S1 fill:#1e293b,color:#7dd3fc,stroke:#334155 style S2 fill:#1e293b,color:#7dd3fc,stroke:#334155 style S3 fill:#1e293b,color:#7dd3fc,stroke:#334155 style S4 fill:#1e293b,color:#7dd3fc,stroke:#334155 style S5 fill:#1e293b,color:#86efac,stroke:#334155 style P0 fill:#1e293b,color:#fcd34d,stroke:#334155 style P1 fill:#1e293b,color:#7dd3fc,stroke:#334155 style P2 fill:#1e293b,color:#7dd3fc,stroke:#334155 style P3 fill:#1e293b,color:#7dd3fc,stroke:#334155 style P4 fill:#1e293b,color:#7dd3fc,stroke:#334155 style P5 fill:#1e293b,color:#fcd34d,stroke:#334155 style P6 fill:#1e293b,color:#86efac,stroke:#334155

Practical Limits

Cost per instance

Each Claude instance consumes tokens independently. 10 parallel instances cost ~10x as much as one sequential instance for the same total work. The tradeoff is time vs. cost.

For most tasks, the time savings justify the cost. For exploratory or uncertain work, start sequential until the approach is clear.

Context switching overhead for the reviewer

You are still the reviewer. Ten simultaneous PRs means ten context switches for you. The practical sweet spot is 3–7 parallel instances — enough to saturate your review bandwidth without creating a backlog.

Boris runs more because he has a systematic review process. Until that is established, start at 3 and increase as your review rhythm improves.

Avoiding conflicts

The most common mistake is giving two Claude instances overlapping file ownership.

# Risky — both might touch shared utilities
Claude 1: "Add user notifications"
Claude 2: "Add email service"
# Both might modify: lib/email.ts, utils/format.ts
# Safe — explicit boundaries
Claude 1: "Add user notifications UI components in components/notifications/"
Claude 2: "Add email service in services/email/ — do not touch components/"

Be explicit about file ownership in each brief. If a shared file needs changes, give it to one instance and have the other import the result.

Boris’s Session Organization

Boris labels sessions by feature area and keeps a running list of what each is working on. When a session completes:

  1. Notification appears in the terminal or Desktop sidebar
  2. Boris reviews the diff briefly (not a full code review yet — just a sanity check)
  3. CI runs on the PR
  4. Full review happens when CI is green

The review does not block other sessions. While Boris reviews PR #143, Claudes #2 through #8 are still running.