Software Architecture for Parallel AI

Software Architecture for Parallel AI

← Go back

Designing Software Architecture for Parallel AI Sessions

To get the most out of Claude Code, we need to run multiple sessions in parallel. And yeah, that gets complex fast. So let's figure out how to structure our files and directories to actually make this work... plus the whole GitHub workflow thing.

image

The Basic Idea: Worktrees Are Your Friend

Okay, so here's the thing about parallel Claude sessions. Each one needs its own space to work. Git worktrees give you exactly that – think of them as separate copies of your project that somehow magically share the same history.

You can have five Claude sessions cranking away on five different features. All from the same repo. It's actually pretty wild when you see it working.

But wait, it gets better. Each session can spawn what I call sub-agents. Like, your main Claude working on authentication might spin up a helper just for database stuff, another one for tests, maybe one more for docs. The worktree setup naturally handles this whole hierarchy thing.

Setting Up Your Repository (The Right Way)

First things first... you need to organize your code so these parallel sessions don't step all over each other. Here's what I've found works:

/apps/
  web/
  admin/
  worker/
/packages/
  ui/
  auth/
  data/
/services/
  billing/
  notifications/
/platform/
  infra/
  scripts/
  schemas/
/agents/
  templates/
  contracts/
  tools/

Simple rule: stuff that changes together lives together. Everything else? Keep it separate. Each folder becomes a boundary – the auth Claude stays in auth, the UI Claude stays in UI. They don't even know each other exist.

That /agents/ folder is special though. That's where you keep your templates for spawning sub-agents. More on that in a minute...

The Sub-Agent Thing (This Is Where It Gets Cool)

So sub-agents. Think of them as Claude's little helpers. They're basically Claude sessions that your main Claude spawns to handle specific jobs. And they work within even tighter boundaries than their parent.

Let me give you a real example. Say you're building authentication. Your main Claude owns /packages/auth/ but it spawns three helpers:

  1. Migration buddy – only touches /packages/auth/migrations/
  2. Test writer – stays in /packages/auth/__tests__/
  3. API specialist – works in /packages/auth/api/

Each one gets its own little workspace inside the parent worktree:

../wt-auth-feature/
  packages/auth/          # Main Claude works here
  .sub-agents/
    migration/           # Helper 1's space
    testing/             # Helper 2's space
    api/                 # Helper 3's space

It's like... giving each assistant their own desk in the office. They can't mess with each other's stuff.

Actually Setting This Up (With Commands That Work)

Creating a worktree with sub-agent support isn't hard. Really:

git worktree add ../wt-auth-feature -b feature/auth-hardening
cd ../wt-auth-feature
mkdir -p .sub-agents

For JavaScript projects, each workspace needs its own node_modules. (I learned this the hard way...)

cd ../wt-auth-feature
pnpm install

# For the sub-agents
cd .sub-agents/testing
ln -s ../../package.json .
pnpm install --filter "*test*"

Python? Same idea, different commands:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Docker Services (Because of Course You Need Them)

If you're using Docker – and let's be honest, you probably are – each worktree needs its own containers. Here's the trick that took me forever to figure out:

name: ${COMPOSE_PROJECT_NAME:-${PWD##*/}}
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: ${COMPOSE_PROJECT_NAME}_app
    ports:
      - "5432"

  # This one's just for tests
  test-db:
    image: postgres:16
    environment:
      POSTGRES_DB: ${COMPOSE_PROJECT_NAME}_test
    ports:
      - "5433"

This automatically namespaces everything by the worktree directory. No conflicts. No "port already in use" errors. Just... works.

Writing Contracts (So Claude Knows What's What)

Every Claude session needs boundaries. Crystal clear ones. I create a SESSION.md file in each worktree:

# What This Session Can Do
- Modify: /packages/auth
- Don't touch: /services/*, /packages/ui
- Can spawn helpers: yes
- Max helpers: 3

# Helper Permissions
- migration-agent: /packages/auth/migrations/**
- test-agent: /packages/auth/__tests__/**
- api-agent: /packages/auth/api/**

# What We're Building
- All helpers finish their tasks
- Tests pass
- No wandering outside boundaries

It's like... giving someone renovation permissions for specific rooms in your house. "You can redo the kitchen, but don't touch the bathroom."

Making Sub-Agent Templates

Store reusable contracts in /agents/templates/. These are like job descriptions for your helpers:

# Test Writer Contract
## Your Job
- Write tests for: {{module}}
- Work in: {{module}}/__tests__/
- Coverage goal: 80%

## Rules
- Only create or edit test files
- Don't change the actual code
- Use existing test helpers

## You Can Read
- Source code: {{module}}/src/**
- Test utilities: /platform/testing/

Actually Spawning These Helpers

Here's a script that makes spawning sub-agents dead simple:

#!/usr/bin/env bash
# spawn-helper.sh

HELPER_TYPE=$1  # like "test" or "migration"
PARENT_DIR=$(pwd)
SUB_DIR=".sub-agents/$HELPER_TYPE"

mkdir -p "$SUB_DIR"
cd "$SUB_DIR"

# Give it its contract
cp "/agents/templates/${HELPER_TYPE}-agent.md" "./CONTRACT.md"

echo "Starting helper: $HELPER_TYPE"
claude-code --contract CONTRACT.md

Keeping Everyone Coordinated

The main Claude needs to know what its helpers are doing. I use a simple coordination file:

# .sub-agents/status.yaml
helpers:
  migration:
    status: working
    task: "Adding user_roles table"
    started: 10 minutes ago

  test:
    status: done
    task: "Auth service tests"
    coverage: 85%

  api:
    status: waiting
    task: "Update OpenAPI spec"
    waiting_for: migration

Making Sure Nobody Colors Outside the Lines

This is crucial. You need automatic guards. Here's a pre-commit hook that's saved my bacon more times than I can count:

#!/usr/bin/env bash
ALLOWED=.allowed-paths

if [ -f "$ALLOWED" ]; then
  violations=$(git diff --cached --name-only | grep -v -f "$ALLOWED" || true)
  if [ -n "$violations" ]; then
    echo "Whoa! You're changing files outside your area:"
    echo "$violations"
    exit 1
  fi
fi

How Helpers Talk to Each Other

Sub-agents don't modify each other's code directly. They leave messages:

# .sub-agents/messages/migration-done.yaml
from: migration
to: test
message: "New user_roles table is ready"
what_changed:
  - User model now has roles
  - Check the new schema version

The main Claude watches these messages and coordinates responses. It's like... being a manager who actually reads their emails.

Your Daily Workflow (What This Actually Looks Like)

Here's how I work with this setup every day:

Morning: Spin up worktrees for today's tasks

./platform/scripts/wt-new-with-agents auth-feature "test,migration"
./platform/scripts/wt-new-with-agents ui-polish "test,docs"

Start main Claudes:

cd ../wt-auth-feature
claude-code

Let them spawn helpers as needed. Check in periodically:

./check-status.sh  # Shows what everyone's doing

When stuff's done, merge it:

git push origin feature/auth-feature
# Make PR, review, merge

Clean up:

./platform/scripts/wt-rm ../wt-auth-feature

Different Types of Helpers (And When to Use Them)

I've found certain patterns work really well:

Test Writer: Just writes tests. Nothing else.

  • Lives in __tests__/
  • Reads source code
  • Can't change functionality

Doc Updater: Keeps documentation in sync

  • Updates READMEs
  • Fixes comments
  • Updates API docs

Refactorer: Makes code prettier without breaking it

  • Runs linters
  • Applies consistent formatting
  • Simplifies complex functions

Performance Tuner: Makes specific code faster

  • Profiles bottlenecks
  • Optimizes algorithms
  • Never changes APIs

The Hierarchy Thing (Don't Go Crazy)

Helpers can have helpers. But... don't go nuts. I've tried four levels deep and it's a mess. Three levels max:

Main Claude: Building the feature
├── Helper: API design
│   └── Sub-helper: Generate types
├── Helper: Implementation
│   └── Sub-helper: Database queries
└── Helper: Testing
    └── Sub-helper: Test data setup

Each level gets tighter boundaries. It's like Russian nesting dolls, but with permissions.

Making CI/CD Not Explode

With all these agents making commits, your CI needs to be smart:

# Only run what's needed
name: Smart CI
on:
  push:
    paths:
      - '**/__tests__/**'  # Test changes
      - '**/migrations/**' # Database changes

jobs:
  quick-check:
    # Just run affected tests
    run: pnpm test --changed

Watching Everything (Without Going Insane)

I have a simple script that shows me what's happening:

#!/usr/bin/env bash
echo "=== Who's Working on What ==="

for worktree in $(git worktree list --porcelain | grep "^worktree"); do
  # Show main Claude
  echo "Main: $(basename $worktree)"

  # Show helpers
  if [ -d "$worktree/.sub-agents" ]; then
    ls -la "$worktree/.sub-agents" | grep ^d
  fi
done

Common Mistakes (That I've Made So You Don't Have To)

Too many tiny helpers. If a task takes less than 15 minutes, just let the main Claude do it.

Circular dependencies. Helper A waits for Helper B waits for Helper A. Dead in the water.

No timeouts. Sometimes helpers get stuck. Auto-kill them after an hour.

Forgetting boundaries. That's why the automated guards are mandatory, not optional.

The Checklist (Before You Start)

Before you dive into this:

Your repo has clear module boundaries
Helper templates exist in /agents/templates/
You have coordination files set up
Message passing works
Boundary guards are in place
CI won't freak out
You can monitor everything
Cleanup scripts exist

Why This Actually Matters

Look, I've been doing this for a while now. The difference is... staggering. It's not just about speed (though yeah, shipping 5x faster is nice). It's about being able to tackle complex stuff that would normally need a whole team.

You become less of a coder and more of a conductor. You're orchestrating these specialized helpers, each doing what they're best at. The test writer writes better tests than I do. The doc helper never forgets to update the README. The refactorer catches stuff I'd miss.

Start simple. Two parallel sessions. Once that feels natural, add a helper or two. Before you know it, you're running a whole symphony of Claude sessions, each playing their part perfectly.

The future isn't just AI helping you code. It's you conducting an orchestra of specialized AI agents, each focused on what they do best. And honestly? It's kind of amazing to watch it all come together.

More about me

My aim is to live a balanced and meaningful life, where all areas of my life are in harmony. By living this way, I can be the best version of myself and make a positive difference in the world. Professionally, I focus on the design and implementation of cognitive infrastructure: systems that make complex enterprise data usable through AI-powered tools and human-like interaction.

About me →

Similar blog posts