Skip to main content
Skip to main content

How We 10x Development with Worktree + Conductor Setup

ExpoConductorWorktreeProductivityGitClaude CodeAI
Guy Serfaty, Software Developer
Jan 22nd, 20264 min read

Git worktrees are powerful. Conductor makes them practical. But the real question is: is your project actually ready for isolated parallel work?

The Problem

Most projects assume a single running instance: one database, fixed ports, one emulator. Try running three features in parallel and you hit conflicts immediately.

The Solution

Each workspace gets its own everything. Here's what runs on yarn setup:

# scripts/setup-workspace.sh

# Unique database name from workspace directory
WORKSPACE_NAME=$(basename "$PROJECT_ROOT")
DB_NAME="ws_${WORKSPACE_NAME//[^a-zA-Z0-9]/_}"

# Get workspace-specific ports
source "$SCRIPT_DIR/lib/ports.sh"
get_workspace_ports "$WORKSPACE_NAME"

# Create database
psql -c "CREATE DATABASE \"$DB_NAME\";"

# Generate .env with workspace-specific config
cat > "$BACKEND_DIR/.env" << EOF
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/${DB_NAME}"
PORT=$BACKEND_PORT
EOF

Port Allocation

Deterministic ports based on workspace name hash—same workspace always gets same ports:

# scripts/lib/ports.sh

hash_string() {
    local str="$1" hash=0
    for ((i=0; i<${#str}; i++)); do
        char="${str:$i:1}"
        ascii=$(printf '%d' "'$char")
        hash=$((hash + ascii))
    done
    echo $((hash % 100))
}

get_workspace_ports() {
    local offset=$(hash_string "$1")
    BACKEND_PORT=$((3100 + offset))
    METRO_PORT=$((8100 + offset))
}

Emulator Management

The critical piece for Expo. Global config tracks which workspace owns which simulator:

# scripts/dev-ios.sh

GLOBAL_SIMULATOR_CONFIG="$HOME/.config/conductor/ios-simulators.json"

# Find simulator not used by other workspaces
find_available_simulator() {
    local assigned_sims=$(get_assigned_simulators)
    for sim in "${AVAILABLE_SIMULATORS[@]}"; do
        # Skip if assigned to another workspace
        if ! echo "$assigned_sims" | grep -q "$sim"; then
            echo "$sim"
            return
        fi
    done
}

# Rename for visual clarity
rename_simulator "$SIMULATOR_UDID" "$SIMULATOR_NAME" "$WORKSPACE_NAME"
# "iPhone 15" becomes "iPhone 15 (feature-branch)"

Cleanup

When archiving a workspace, everything needs to be released:

# scripts/cleanup.sh

# Safety: only drop ws_ prefixed databases
if [[ ! "$DB_NAME" =~ ^ws_ ]]; then
    log_error "Refusing to drop database for safety"
    exit 1
fi

# Kill processes by port
kill_port $BACKEND_PORT "Backend"
kill_port $METRO_PORT "Metro bundler"

# Shutdown simulator and restore original name
xcrun simctl shutdown "$SIMULATOR_UDID"
xcrun simctl rename "$SIMULATOR_UDID" "$ORIGINAL_NAME"

# Remove from global config (frees simulator for other workspaces)
python3 -c "
import json
with open('$GLOBAL_SIMULATOR_CONFIG', 'r') as f:
    data = json.load(f)
del data['$WORKSPACE_NAME']
with open('$GLOBAL_SIMULATOR_CONFIG', 'w') as f:
    json.dump(data, f, indent=2)
"

# Drop database
psql -c "DROP DATABASE \"$DB_NAME\";"

Conductor Integration

In Conductor UI, configure the repo hooks:

EventScript
On workspace createyarn setup
On workspace archiveyarn cleanup

Now workspace lifecycle is fully automated. Create a workspace in Conductor—database provisioned, ports allocated, emulator assigned. Archive it—everything cleaned up, resources freed for the next workspace.

The Interface

{
  "scripts": {
    "setup": "./scripts/setup-workspace.sh",
    "cleanup": "./scripts/cleanup.sh",
    "dev": "./scripts/dev.sh",
    "dev:ios": "./scripts/dev-ios.sh"
  }
}

Get the Full Scripts

The snippets above are simplified. For complete, drop-in scripts you can customize for your project:

Download from GitHub Gist

Includes all helper functions, error handling, and configuration options.

In Practice

Three features, three workspaces, three emulators running simultaneously. Review changes, create PR, archive workspace. No port conflicts. No shared state. No manual cleanup.


Choosing the right tools isn't enough—you need the right setup.

Want to 10x your development with a setup built for parallel work? .

You might also like

;