Monorepo Orchestration Setup
Overview
Configure and orchestrate monorepos for scalable multi-project development. This skill covers workspace setup, task pipeline configuration, incremental builds, and CI/CD integration using modern monorepo tooling (Nx, Turborepo, Lerna).
REQUIRED: superpowers:verification-before-completion
When to Use
- •Setting up a new monorepo from scratch
- •Migrating from polyrepo to monorepo architecture
- •Adding monorepo tooling (Nx, Turborepo, Lerna) to existing repository
- •Configuring task pipelines and build orchestration
- •Optimising build performance with caching strategies
- •Setting up CI/CD for monorepo workflows
- •User asks about "workspace configuration" or "multi-package repository"
Detection and Deference
Before creating new monorepo configuration, check for existing setup:
# Check for existing monorepo tools ls nx.json turbo.json lerna.json pnpm-workspace.yaml 2>/dev/null # Check for existing workspace configuration ls package.json && grep -l "workspaces" package.json 2>/dev/null # Check for existing project structure ls packages/ apps/ libs/ projects/ 2>/dev/null
If existing configuration found:
- •Build upon existing monorepo setup
- •Use existing tool conventions
- •Reference existing workspace structure
If no configuration found:
- •Help select appropriate monorepo tool
- •Create minimal initial configuration
- •Document tool selection in ADR
Tool Selection Guide
Select monorepo tooling based on ecosystem and requirements:
| Tool | Best For | Key Features |
|---|---|---|
| Nx | Large enterprise, full-stack | Plugins, generators, affected graph |
| Turborepo | JavaScript/TypeScript focused | Simple config, fast caching |
| Lerna | npm package publishing | Version management, publishing |
Selection Criteria
Choose Nx when:
- •Building full-stack applications (frontend + backend)
- •Need code generators and scaffolding
- •Require advanced dependency graph analysis
- •Team benefits from opinionated structure
Choose Turborepo when:
- •Primary focus is JavaScript/TypeScript
- •Want minimal configuration overhead
- •Need fast remote caching
- •Prefer flexibility over structure
Choose Lerna when:
- •Managing multiple npm packages for publishing
- •Need version synchronisation across packages
- •Publishing to npm registry is primary goal
- •Already using Lerna (migration cost consideration)
Core Workflow
- •Assess requirements (team size, project types, CI/CD needs)
- •Select monorepo tool based on ecosystem and requirements
- •Configure workspace with appropriate project structure
- •Define task pipelines (build, test, lint dependencies)
- •Enable caching (local and remote)
- •Configure CI/CD with affected-only execution
- •Document conventions and onboarding guide
Configuration Patterns
Workspace Structure
Standard monorepo directory layout:
monorepo/
apps/ # Deployable applications
web/
api/
packages/ # Shared libraries
ui/
utils/
tools/ # Build tooling and scripts
nx.json # or turbo.json
package.json # Root package with workspaces
Task Pipeline Configuration
Define task dependencies to enable incremental builds:
Nx (nx.json):
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true
},
"test": {
"dependsOn": ["build"],
"cache": true
},
"lint": {
"cache": true
}
}
}
Turborepo (turbo.json):
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": []
},
"lint": {
"outputs": []
}
}
}
Caching Strategies
Enable caching for reproducible, fast builds:
Local caching:
- •Enabled by default in Nx and Turborepo
- •Cache stored in
.nx/cacheornode_modules/.cache/turbo - •Clear with
nx resetorturbo run build --force
Remote caching:
- •Nx Cloud:
npx nx connect - •Vercel Remote Cache:
turbo loginandturbo link - •Self-hosted: Configure cache storage endpoint
CI/CD Integration
Run only affected projects in CI:
GitHub Actions (Nx):
- name: Build affected run: npx nx affected -t build --base=origin/main - name: Test affected run: npx nx affected -t test --base=origin/main
GitHub Actions (Turborepo):
- name: Build run: npx turbo run build --filter=...[origin/main] - name: Test run: npx turbo run test --filter=...[origin/main]
Common Pitfalls
Pitfall 1: Over-coupling packages
Problem: Too many dependencies between packages slow builds.
Solution: Design packages with clear boundaries. Use dependency graph visualisation
to identify problematic coupling (nx graph or turbo run build --graph).
Pitfall 2: Missing task dependencies
Problem: Tasks run in wrong order causing failures.
Solution: Explicitly define dependsOn for all tasks that require outputs from
other tasks. Test with clean cache.
Pitfall 3: Cache invalidation issues
Problem: Stale cache causes incorrect builds.
Solution: Define all outputs and inputs correctly. Use hashing configuration to include all relevant files. When in doubt, clear cache and rebuild.
Pitfall 4: Slow CI without affected filtering
Problem: CI rebuilds everything on every commit.
Solution: Use affected commands with base branch comparison. Configure proper base branch for PRs and main branch builds.
Verification Checklist
Before declaring setup complete, verify:
- • Workspace configuration is valid (
nx show projectsorturbo run build --dry-run) - • Task pipelines execute in correct order
- • Caching works correctly (second run is fast)
- • Affected detection works (only changed projects run)
- • CI/CD pipeline executes affected tasks only
- • Team onboarding documentation exists
- • Architecture decision documented (tool selection rationale)
Quick Reference Commands
Nx
# Create workspace npx create-nx-workspace@latest myworkspace # Generate application npx nx g @nx/web:app myapp # Generate library npx nx g @nx/js:lib mylib # Run affected npx nx affected -t build test lint # Visualise dependency graph npx nx graph
Turborepo
# Create workspace npx create-turbo@latest # Run all builds npx turbo run build # Run affected builds npx turbo run build --filter=...[origin/main] # Visualise dependency graph npx turbo run build --graph
Lerna
# Initialise Lerna npx lerna init # Bootstrap packages npx lerna bootstrap # Run script in all packages npx lerna run build # Run only in changed packages npx lerna run build --since main # Version and publish npx lerna version npx lerna publish
Red Flags - STOP
These statements indicate monorepo anti-patterns:
| Thought | Reality |
|---|---|
| "Everything in one repo is simpler" | Without proper tooling, monorepos become unmanageable; use orchestration |
| "We don't need affected detection" | Full builds on every commit waste CI time exponentially; enable filtering |
| "Caching isn't worth the setup" | Remote caching can reduce build times by 70%+; invest in setup |
| "Packages can depend on anything" | Unconstrained coupling kills build performance; define boundaries |
| "We'll optimise builds later" | Build performance debt compounds quickly; configure pipelines early |
| "Task ordering doesn't matter" | Missing dependencies cause flaky builds; explicitly define dependsOn |
Integration with Other Skills
- •walking-skeleton-delivery: Use monorepo structure for E2E skeleton across services
- •ci-cd-conformance: Configure CI/CD pipelines with affected-only execution
- •automated-standards-enforcement: Apply linting and formatting across all packages
Evidence Examples
Nx Graph Output
npx nx graph --file=output.json
Sample output.json (dependency graph):
{
"graph": {
"nodes": {
"web": {
"name": "web",
"type": "app",
"data": { "root": "apps/web" }
},
"api": {
"name": "api",
"type": "app",
"data": { "root": "apps/api" }
},
"ui": {
"name": "ui",
"type": "lib",
"data": { "root": "packages/ui" }
},
"utils": {
"name": "utils",
"type": "lib",
"data": { "root": "packages/utils" }
}
},
"dependencies": {
"web": [
{ "source": "web", "target": "ui", "type": "static" },
{ "source": "web", "target": "utils", "type": "static" }
],
"api": [{ "source": "api", "target": "utils", "type": "static" }],
"ui": [{ "source": "ui", "target": "utils", "type": "static" }],
"utils": []
}
}
}
Turborepo Graph Output
$ npx turbo run build --graph=graph.html # Or JSON format: $ npx turbo run build --graph=graph.json
Sample turbo output (task execution):
• Packages in scope: @myorg/api, @myorg/ui, @myorg/utils, @myorg/web • Running build in 4 packages • Remote caching disabled @myorg/utils:build: cache hit, replaying output @myorg/ui:build: cache miss, executing @myorg/api:build: cache hit, replaying output @myorg/web:build: cache miss, executing Tasks: 4 successful, 4 total Cached: 2 cached, 4 total Time: 3.245s
Affected Detection Evidence
# Nx affected output $ npx nx affected:apps --base=main --head=HEAD > NX Affected apps: - web $ npx nx affected:libs --base=main --head=HEAD > NX Affected libs: - ui - utils # Turborepo affected output $ npx turbo run build --filter='...[origin/main]' --dry-run • Packages in scope: @myorg/ui, @myorg/web • Running build in 2 packages • 2 packages in graph are affected by changes
Baseline Performance Metrics
Metrics to Capture
| Metric | Description | Target |
|---|---|---|
| Cold build time | Full build with empty cache | Baseline |
| Warm build time | Full build with populated cache | < 10% of cold |
| Affected build time | Build only changed packages | < 30% of cold |
| Cache hit rate | Percentage of cached tasks | > 80% in CI |
Performance Worksheet
# Monorepo Performance Baseline **Repository**: [repo-name] **Date**: YYYY-MM-DD **Tool**: [Nx/Turborepo/Lerna] **Packages**: [N] apps, [M] libs ## Build Performance | Scenario | Time | Cache Status | Notes | | ---------------- | ---- | ------------ | -------------------------------------- | | Cold build (all) | [X]s | No cache | `nx run-many -t build --skip-nx-cache` | | Warm build (all) | [X]s | Full cache | Second run of above | | Affected (1 lib) | [X]s | Partial | After changing utils | | Affected (1 app) | [X]s | Partial | After changing web only | ## CI Performance | Metric | Before Optimization | After | Improvement | | ------------------ | ------------------- | ----- | ----------- | | Average PR build | [X]m | [Y]m | [Z]% | | Cache hit rate | [X]% | [Y]% | +[Z]% | | Affected detection | [X]s | [Y]s | [Z]% | ## Verification Commands # Measure cold build time npx nx run-many -t build --skip-nx-cache # Measure warm build (run twice, take second) npx nx run-many -t build time npx nx run-many -t build # Measure affected build git checkout -b test-affected echo "// change" >> packages/utils/src/index.ts time npx nx affected -t build --base=main git checkout main && git branch -D test-affected # Check cache statistics (Nx) npx nx show cache-stats # Check cache statistics (Turborepo) npx turbo run build --summarize
Sample CI Evidence
# GitHub Actions summary output Build Summary: Total packages: 12 Affected packages: 3 Cached tasks: 9/12 (75%) Build time: 45s (vs 180s full build) Cache savings: 135s (75% reduction)