Published on March 6, 2026 • 12 min read
For most of my career, I shipped features one at a time. Pick a ticket, implement it, open a PR, wait for review, merge, repeat. It worked fine — until I started working on larger product pushes where I'd have 10 or 15 independent tickets queued up at once.
The bottleneck wasn't my coding speed. It was that everything was serial. Ticket 4 couldn't start until ticket 3 was done, even when they had nothing to do with each other. I was the constraint.
A few months ago I started experimenting with running multiple Claude Code agents in parallel, each owning an independent ticket. What I found surprised me: not only did it work, it scaled better than I expected — and the failure modes were more manageable than I feared.
Here's what that setup looks like now, what works well, and where I still run into friction.
When you have a batch of independent work, serial execution is genuinely wasteful. If I'm adding a new article, updating a data file, and adding a showcase entry — and none of those touch the same files — there's no technical reason they need to happen sequentially.
But it's not just time. Serial execution also compresses context. I'd finish ticket 7, my brain loaded with its details, and immediately pivot to ticket 8 which needed completely different context. That context-switching cost adds up.
The ideal is something closer to how a small team works: multiple people working in parallel on independent pieces, with a coordinator making sure nothing conflicts.
What I built is essentially a structured orchestration layer over Claude Code. The flow looks like this:
graph TD
A[Linear Tickets] --> B[Dependency Analysis]
B --> C[Prompt File Generation]
C --> D[Agent Scaffold — inject real code context]
D --> E[Orchestrator]
E --> F[Phase A Agents — parallel]
E --> G[Phase B Agents — parallel, after A]
F --> H[Git Push]
G --> H
H --> I[Verification — build, lint, test]
I --> J[PR Creation]
J --> K[Auto-merge or Human Review]
It starts with Linear tickets. The orchestrator reads a set of tickets, analyzes their dependencies, and groups them into phases. Phase A tickets have no dependencies and can run immediately. Phase B tickets depend on Phase A output and wait.
For each ticket, a Claude Code agent gets a structured prompt that includes:
Each agent runs in isolation. When it's done, it commits, pushes its branch, and stops. The orchestrator collects the branches, runs verification, creates PRs, and (optionally) merges them.
The thing that makes parallel agents actually work without conflicts is git worktrees.
Each agent gets its own working directory via git worktree add:
git worktree add ../portfolio-workspace-FRE-69 \
-b feature/FRE-69-ai-agents-article-content \
develop
This creates a separate checkout of the repo in a sibling directory. The agent does all its work there — reads files, writes files, commits — completely isolated from every other agent's working directory. No shared filesystem state, no merge conflicts mid-implementation.
Without worktrees, two agents writing to the same repo simultaneously would corrupt each other's state. With worktrees, they're working in completely separate directories backed by the same git object store.
After an agent pushes and the PR is merged, the worktree is removed:
git worktree remove ../portfolio-workspace-FRE-69
This is one of those things that sounds obvious in retrospect but took me a while to land on. Before worktrees, I tried running agents sequentially-but-faster, which defeated the point, or running them in different clones, which created sync headaches.
Not all tickets are truly independent. In a typical batch, maybe 60% have no dependencies, 30% depend on one earlier ticket, and 10% have complex dependencies.
The prompt file format I use encodes this explicitly:
phases:
A:
prompts: [1, 2, 3, 4]
parallel: true
depends_on_phases: []
B:
prompts: [5, 6, 7]
parallel: true
depends_on_phases: [A]
The orchestrator reads this, launches all Phase A agents simultaneously, waits for them all to complete successfully, then launches Phase B. Within each phase, agents run in parallel. Between phases, there's a synchronization point.
This handles most real-world dependency structures. Occasionally I need three phases, but I haven't needed more than that on any project so far.
The run that convinced me this was worth investing in was a portfolio consolidation batch — 9 tickets across 4 phases. The tickets covered things like:
Running them serially would have taken me most of a day. The parallel run completed all 9 in roughly the time of 2-3 tickets run sequentially.
The more interesting result was quality. Because each agent had a focused, single-ticket prompt with injected code context, the implementations were more consistent than my own serial work — I'd sometimes drift from patterns when I was tired or context-switched. The agents didn't drift; they followed the patterns I gave them.
Separately from the workspace orchestrator I described above, I built a more self-contained version called kitchen-core-agent-pipeline. It has two deployment variants:
GitHub Actions variant ($0/mo): A webhook listener triggers a GitHub Actions workflow when a Linear ticket is moved to "In Progress." The workflow spins up a Claude Code agent, runs it with a configured max-turns limit, and opens a draft PR when complete.
n8n variant (~$10/mo on a VPS): An n8n workflow polls Linear for ready tickets on a schedule, processes them in batches with slot-based throttling, and handles retries with exponential backoff. More control, more moving parts.
The GitHub Actions version is better for individuals or small teams — zero infrastructure overhead, runs on GitHub's compute. The n8n version is better when you need more control over concurrency, retry logic, or integration with other tools in your stack.
Both variants create draft PRs by default. The agent doesn't merge anything — it implements, opens a PR, and waits. Human review is still the gate before merge.
Running AI agents autonomously against a real codebase isn't something I'd do without guardrails. The ones I've found essential:
Max-turns limits: Claude Code accepts a --max-turns flag. Without it, an agent that gets confused can spin indefinitely. I set this to 40-60 turns depending on ticket complexity.
Timeout enforcement: Separate from max-turns, a wall-clock timeout kills an agent that's stalled. Agents sometimes get into states where they're making progress by some metric but not converging on a solution.
No-change detection: If an agent completes without any file changes, something went wrong. The pipeline detects this and marks the ticket as failed rather than creating an empty PR.
Draft PRs: All agent-created PRs are drafts. This prevents accidental auto-merge by CI/CD tooling and makes clear the PR needs human eyes.
Exponential backoff on retry: When an agent fails (build error, lint failure, unexpected output), the pipeline retries with increasing delays. This prevents hammering the API on a transient failure.
Verification step: Before the PR is created, a separate verification run checks that build passes, lint passes, no debug statements were left in, and the implementation actually touches the files the ticket described. I've caught a few cases where an agent completed without errors but implemented something in the wrong location.
This setup is worth the overhead for batches of 5+ independent tickets. Below that threshold, the setup cost (writing prompt files, scaffolding context, monitoring agents) isn't worth it.
Specifically, I avoid parallel agents for:
The pattern that works best: I design the approach, define the tickets with enough specificity that the implementation is mostly mechanical, then let agents handle the mechanical part.
One thing I underestimated early on was how much agent output quality depends on context quality. An agent with a vague prompt produces vague output. An agent with the exact file path, the existing pattern to follow, and a verification command produces something I can merge with minor adjustments.
The scaffold step in the pipeline does this injection automatically. It reads the relevant pattern files and contract docs, finds the most relevant existing code, and inlines it into the prompt:
## Existing Pattern (follow exactly)
File: src/app/articles/page.tsx, lines 21-82
[injected code snippet]
## File to create
src/content/articles/ai-agents-parallel-development.md
## Verification
npm run build && npm run lint
The difference between a prompt with this context and one without it is significant. Without context, agents default to generic patterns they've seen in training data. With context, they match the codebase's actual conventions.
For reference, here's how the full pipeline fits together:
graph LR
L[Linear Tickets] --> CPF[/create-prompt-file/]
CPF --> PF[prompts-name.md]
PF --> APS[/agent-prompt-scaffold/]
APS --> EPF[Enriched Prompt File]
EPF --> OP[/orchestrate-prompts/]
OP --> A1[Agent 1 — worktree]
OP --> A2[Agent 2 — worktree]
OP --> A3[Agent N — worktree]
A1 --> VER[/agent-verify/]
A2 --> VER
A3 --> VER
VER --> PR[PRs Created]
PR --> MERGE[Auto-merge or Review]
The four commands in the pipeline (/create-prompt-file, /agent-prompt-scaffold, /orchestrate-prompts, /agent-verify) each do one thing and can be run independently. I sometimes skip scaffold for simple tickets, or skip orchestrate and run one agent manually if I want to supervise it more closely.
A few things I'd change if I were starting over:
Write prompts at ticket creation time, not at execution time. When I'm deep in the design of a feature, I have the most context for writing a good agent prompt. By the time I run orchestration, I've partially forgotten the nuances.
Keep phases smaller. My early batches had large phases (8-10 agents). Smaller phases (3-4 agents) with more synchronization points give better error isolation — when something goes wrong, you know more precisely where.
Don't skip verification. Early on I sometimes skipped the verification step to save time. The cases where something slipped through cost more time to fix than the verification would have taken.
Parallel AI agent development isn't magic, and it doesn't replace the design and architectural thinking that makes features good. What it does is let the mechanical implementation work happen in parallel, which meaningfully compresses calendar time on batches of independent tickets.
The setup overhead is real. But once the pipeline is in place, adding a new batch of tickets is mostly prompt-writing and context injection — not infrastructure work.
For me, the biggest benefit hasn't been raw speed. It's that I can queue up a large batch of work, start the pipeline, and come back to a set of PRs ready for review — rather than spending a day in context-switching mode bouncing between tickets.
Building something where this kind of automation might help? Get in touch — I'm always interested in talking through orchestration patterns for specific codebases and team structures.