| name | monorepo-tooling |
| description | Use when setting up monorepo tooling, optimizing builds, or migrating between tools with Turborepo, Nx, Bazel, Lerna for efficient task running, caching, and code generation. |
| allowed-tools | ["Read","Write","Edit","Bash","Glob","Grep"] |
Monorepo Tooling Skill
Overview
This skill provides comprehensive guidance on monorepo build systems, task
runners, package managers, and development tools that enable efficient
development, building, and testing across multiple packages in a monorepo.
Build Systems
Turborepo
High-performance build system with intelligent caching and task orchestration.
Pipeline Configuration
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [
".env",
"tsconfig.json"
],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [
"dist/**",
".next/**",
"build/**"
],
"cache": true
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"cache": true
},
"lint": {
"outputs": [],
"cache": true
},
"dev": {
"cache": false,
"persistent": true
},
"deploy": {
"dependsOn": ["build", "test", "lint"],
"cache": false
}
},
"globalEnv": [
"NODE_ENV",
"CI"
]
}
Remote Cache Configuration
{
"remoteCache": {
"enabled": true
}
}
With Vercel:
turbo login
turbo link
With custom cache:
{
"remoteCache": {
"enabled": true,
"signature": true,
"preflight": true
}
}
Key Features
- Incremental builds: Only rebuild changed packages
- Remote caching: Share cache across team and CI
- Parallel execution: Run tasks in parallel when safe
- Pipeline dependencies: Automatic task ordering
- Pruning: Extract subset of monorepo
- Filtering: Run tasks on specific packages
Usage:
turbo run build
turbo run build --filter=@myorg/web
turbo run build --filter=@myorg/web...
turbo run build --force
turbo run build --dry-run
turbo prune --scope=@myorg/web
Nx
Extensible build system with powerful code generation and analysis.
Workspace Configuration
{
"extends": "nx/presets/npm.json",
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": [
"build",
"test",
"lint"
],
"parallel": 3,
"cacheDirectory": "node_modules/.cache/nx"
}
}
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"outputs": ["{projectRoot}/dist"]
Project Configuration
{
"name": "web",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/web",
"main": "apps/web/src/main.ts",
"tsConfig": "apps/web/tsconfig.app.json"
}
},
"serve": {
"executor": "@nx/webpack:dev-server",
"options": {
"buildTarget": "web:build"
}
},
"test":
Nx Cloud Configuration
{
"nxCloudAccessToken": "YOUR_ACCESS_TOKEN",
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": {
"cacheableOperations": ["build", "test", "lint"],
"accessToken": "YOUR_ACCESS_TOKEN"
}
}
}
}
Nx Key Features
- Computation caching: Local and remote cache
- Affected commands: Run tasks on changed projects only
- Code generators: Scaffolding for new projects
- Dependency graph: Visualize project relationships
- Module boundaries: Enforce architectural rules
- Distributed execution: Parallel task execution
Usage:
nx run-many --target=build --all
nx affected --target=test --base=main
nx graph
nx generate @nx/js:library my-lib
nx build web
nx reset
Bazel
Google's scalable build system for very large monorepos.
WORKSPACE File
workspace(name = "my_workspace")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "...",
urls = ["https://github.com/bazelbuild/rules_nodejs/..."],
)
load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories")
node_repositories(
node_version = "18.16.0",
package_manager = "pnpm",
)
BUILD File
load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm")
load("@npm//@bazel/typescript:index.bzl", "ts_library")
ts_library(
name = "ui",
srcs = glob(["src/**/*.ts", "src/**/*.tsx"]),
deps = [
"@npm//react",
"@npm//react-dom",
"@npm//@types/react",
],
visibility = ["//visibility:public"],
)
pkg_npm(
name = "ui_pkg",
deps = [":ui"],
package_name = "@myorg/ui",
substitutions = {
"0.0.0-PLACEHOLDER": "{STABLE_VERSION}",
},
)
Bazel Key Features
- Hermetic builds: Reproducible builds
- Remote execution: Distribute builds across machines
- Fine-grained caching: Cache at file level
- Language agnostic: Support many languages
- Scalability: Handle massive codebases
- Build correctness: Reliable dependency tracking
Usage:
bazel build //packages/ui:ui
bazel build //packages/ui/...
bazel test //packages/ui:ui_test
bazel run //apps/web:serve
bazel clean
bazel query 'deps(//packages/ui:ui)'
Lerna
Multi-package repository management and publishing tool.
Lerna Configuration
{
"version": "independent",
"npmClient": "pnpm",
"useWorkspaces": true,
"packages": [
"packages/*",
"apps/*"
],
"command": {
"publish": {
"conventionalCommits": true,
"message": "chore(release): publish",
"ignoreChanges": [
"**/__tests__/**",
"**/*.md"
]
},
"version": {
"allowBranch": ["main", "next"],
Lerna Key Features
- Version management: Fixed or independent versioning
- Publishing: Automated package publishing
- Bootstrap: Link local packages
- Changed detection: Identify modified packages
- Conventional commits: Automated changelogs
- Workspace integration: Works with NPM/Yarn/PNPM
Usage:
lerna bootstrap
lerna run build
lerna run test --since origin/main
lerna publish
lerna publish from-git
lerna version
lerna list
Rush
Scalable monorepo manager with strict dependency management.
Rush Configuration
{
"rushVersion": "5.108.0",
"pnpmVersion": "8.10.0",
"nodeSupportedVersionRange": ">=18.0.0",
"projectFolderMinDepth": 1,
"projectFolderMaxDepth": 2,
"projects": [
{
"packageName": "@myorg/web",
"projectFolder": "apps/web",
"reviewCategory": "production"
},
{
"packageName": "@myorg/ui",
"projectFolder": "packages/ui",
"reviewCategory": "production"
}
]
}
Rush build configuration:
{
"operationSettings": [
{
"operationName": "build",
"outputFolderNames": ["dist", "lib"]
}
]
}
Rush Key Features
- Phantom dependencies prevention: Strict dependency checking
- Subset installs: Install only needed packages
- Change tracking: Detect which projects changed
- Build cache: Share builds across team
- Policy enforcement: Package management rules
- Incremental builds: Only rebuild changed packages
Usage:
rush install
rush update
rush build
rush rebuild
rush my-command
rush publish
Task Running
Parallel Execution
Execute tasks across packages in parallel for speed.
Turborepo parallel execution:
turbo run build
turbo run build --concurrency=4
turbo run build --concurrency=100
Nx parallel execution:
nx run-many --target=build --all
nx run-many --target=build --all --parallel=5
nx run-many --target=build --all --parallel=false
PNPM parallel execution:
pnpm -r run build
pnpm -r --workspace-concurrency=1 run build
pnpm -r --workspace-concurrency=4 run build
Task Dependencies
Define which tasks must complete before others.
{
"pipeline": {
"build": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["build"]
},
"deploy": {
"dependsOn": ["build", "test", "lint"]
}
}
}
Dependency types:
^build - Build dependencies first (topological)
build - Current package build first
["^build", "lint"] - Multiple dependencies
Selective Task Execution
Run tasks on specific packages or changed packages only.
Turborepo filtering:
turbo run build --filter=@myorg/web
turbo run build --filter=@myorg/web...
turbo run build --filter=...@myorg/web
turbo run build --filter=@myorg/web --filter=@myorg/api
turbo run build --filter=[HEAD^1]
Nx affected:
nx affected --target=build
nx affected --target=test --base=main --head=HEAD
nx affected:apps
nx affected:libs
PNPM filtering:
pnpm --filter @myorg/web run build
pnpm --filter @myorg/web... run build
pnpm --filter ...@myorg/web run build
pnpm --filter "[main]" run test
Watch Mode Across Packages
Automatically rebuild on file changes.
{
"scripts": {
"dev": "turbo run dev --parallel",
"dev:web": "turbo run dev --filter=@myorg/web..."
}
}
With concurrently:
{
"scripts": {
"dev": "concurrently \"pnpm:dev:*\"",
"dev:web": "pnpm --filter @myorg/web run dev",
"dev:api": "pnpm --filter @myorg/api run dev"
}
}
With Turborepo watch:
turbo run dev --parallel --no-cache
Caching Strategies
Local Computation Cache
Cache task outputs locally for faster rebuilds.
Turborepo local cache:
{
"pipeline": {
"build": {
"outputs": ["dist/**", ".next/**"],
"cache": true
}
}
}
Cache location: node_modules/.cache/turbo
Nx local cache:
{
"tasksRunnerOptions": {
"default": {
"options": {
"cacheableOperations": ["build", "test", "lint"],
"cacheDirectory": "node_modules/.cache/nx"
}
}
}
}
Cache location: node_modules/.cache/nx
Remote Cache
Share cache across team members and CI environments.
Turborepo Remote Cache with Vercel:
turbo login
turbo link
turbo run build
Nx Cloud:
nx connect-to-nx-cloud
{
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": {
"cacheableOperations": ["build", "test", "lint"],
"accessToken": "YOUR_TOKEN"
}
}
}
}
Custom Remote Cache:
import { createServer } from 'turbo-remote-cache';
createServer({
storage: {
type: 's3',
bucket: 'my-turbo-cache',
region: 'us-east-1'
},
port: 3000
});
Cache Keys and Invalidation
Control what invalidates the cache.
Turborepo cache keys:
{
"globalDependencies": [
".env",
"tsconfig.json",
".eslintrc.js"
],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"inputs": [
"src/**/*.ts",
"src/**/*.tsx",
"package.json"
],
"outputs": ["dist/**"]
}
}
}
Nx cache inputs:
{
"namedInputs": {
"default": ["{projectRoot}/**/*"],
"production": [
"default",
"!{projectRoot}/**/*.spec.ts",
"!{projectRoot}/**/*.test.ts"
],
"sharedGlobals": [
"{workspaceRoot}/tsconfig.base.json",
"{workspaceRoot}/.eslintrc.json"
]
},
"targetDefaults": {
"build": {
"inputs": ["production", "^production", "sharedGlobals"]
}
}
}
Cache invalidation:
turbo run build --force
nx reset
nx build web --skip-nx-cache
Docker Layer Caching
Optimize Docker builds in monorepo context.
# Dockerfile for web app
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
# Copy root package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY .npmrc ./
# Copy package files for dependencies
COPY packages/ui/package.json ./packages/ui/
COPY packages/utils/package.json ./packages/utils/
COPY apps/web/package.json ./apps/web/
# Install dependencies
RUN corepack enable pnpm && pnpm install --frozen-lockfile
# Build stage
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Build with Turbo
RUN pnpm turbo run build --filter=@myorg/web...
# Production stage
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/apps/web/.next ./apps/web/.next
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "apps/web/.next/server.js"]
Code Generation
Nx Generators
Create custom code generators for consistency.
import {
Tree,
formatFiles,
generateFiles,
joinPathFragments
} from '@nx/devkit';
export interface LibraryGeneratorSchema {
name: string;
directory: string;
}
export default async function (tree: Tree, schema: LibraryGeneratorSchema) {
const projectRoot = joinPathFragments('packages', schema.directory);
generateFiles(
tree,
joinPathFragments(__dirname, 'files'),
projectRoot,
{
...schema,
tmpl: ''
}
);
await formatFiles(tree);
}
Template files:
export function <%= name %>() {
return '<%= name %>';
}
{
"name": "@myorg/<%= name %>",
"version": "0.0.1"
}
Usage:
nx generate @myorg/tools:library --name=my-lib --directory=shared
Plop Templates
Use Plop for simpler code generation.
export default function (plop) {
plop.setGenerator('package', {
description: 'Create a new package',
prompts: [
{
type: 'input',
name: 'name',
message: 'Package name:'
},
{
type: 'list',
name: 'type',
message: 'Package type:',
choices: ['library', 'app', 'service']
}
],
actions: [
{
type: 'addMany',
destination: 'packages/{{name}}',
templateFiles: 'templates/package/**/*',
base: 'templates/package'
}
]
});
}
Templates:
{
"name": "@myorg/{{name}}",
"version": "0.0.1",
"type": "{{type}}"
}
Usage:
pnpm plop package
Package Scaffolding
Automate new package creation.
#!/bin/bash
NAME=$1
TYPE=$2
if [ -z "$NAME" ]; then
echo "Usage: create-package.sh <name> <type>"
exit 1
fi
DIR="packages/$NAME"
mkdir -p "$DIR/src"
mkdir -p "$DIR/__tests__"
cat > "$DIR/package.json" << EOF
{
"name": "@myorg/$NAME",
"version": "0.0.1",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"scripts": {
"build": "tsc",
"test": "vitest"
}
}
EOF
echo "export const $NAME = '$NAME';" > "$DIR/src/index.ts"
cat > "$DIR/README.md" << EOF
# @myorg/$NAME
Description of $NAME package.
EOF
echo "Created package: @myorg/$NAME"
Consistent Boilerplate
Maintain consistency with shared templates.
import fs from 'fs';
import path from 'path';
interface ComponentOptions {
name: string;
package: string;
}
function createComponent({ name, package: pkg }: ComponentOptions) {
const dir = path.join('packages', pkg, 'src', 'components', name);
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(
path.join(dir, `${name}.tsx`),
`import React from 'react';
export interface ${name}Props {
children?: React.ReactNode;
}
export function ${name}({ children }: ${name}Props) {
return <div>{children}</div>;
}
`
);
fs.writeFileSync(
path.join(dir, `${name}.test.tsx`),
`import { render } from '@testing-library/react';
import { ${name} } from './${name}';
describe('${name}', () => {
it('renders children', () => {
const { getByText } = render(<${name}>Hello</${name}>);
expect(getByText('Hello')).toBeInTheDocument();
});
});
`
);
fs.(
path.(dir, ),
);
}
Affected Analysis
Git-Based Change Detection
Identify which packages changed based on Git history.
Turborepo:
turbo run build --filter=[HEAD^1]
turbo run build --filter=[HEAD^3]
turbo run build --filter=[origin/main...HEAD]
Nx:
nx affected --target=build --base=main
nx affected --target=test --base=abc123 --head=def456
nx affected:apps
nx affected:libs
Dependency Graph Analysis
Understand project relationships for smarter builds.
Visualize with Nx:
nx graph
nx affected:graph
nx graph --focus=web
Query with Nx:
nx show project web --web
nx show projects
Turborepo graph:
turbo run build --graph
turbo run build --graph=graph.html
Smart Rebuild Strategies
Only rebuild what's necessary based on changes.
Nx affected strategy:
{
"affected": {
"defaultBase": "main"
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"inputs": ["production", "^production"]
}
}
}
Turborepo affected strategy:
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
CI configuration:
- name: Build affected
run: |
turbo run build --filter=[origin/main...HEAD]
Incremental Builds
Build only changed files within packages.
TypeScript incremental builds:
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./dist/.tsbuildinfo"
}
}
Webpack incremental builds:
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}
};
Next.js incremental builds:
module.exports = {
experimental: {
incrementalCacheHandlerPath: './cache-handler.js'
}
};
Package Management
PNPM Workspaces
Fast, disk-efficient package manager with strict dependency model.
packages:
- 'apps/*'
- 'packages/*'
- 'services/*'
shared-workspace-lockfile=true
link-workspace-packages=true
prefer-workspace-packages=true
strict-peer-dependencies=false
auto-install-peers=true
save-workspace-protocol=rolling
Key advantages:
- Content-addressable storage (saves disk space)
- Strict node_modules structure (no phantom dependencies)
- Fastest installation speed
- Built-in monorepo support
- Workspace protocol support
Commands:
pnpm install
pnpm --filter @myorg/web add react
pnpm --filter @myorg/web add @myorg/ui
pnpm --filter @myorg/web update react
pnpm -r run build
pnpm --filter "[main]" run test
Yarn Workspaces
Yarn's workspace implementation with plugin ecosystem.
Yarn Classic:
{
"workspaces": [
"packages/*",
"apps/*"
]
}
Yarn Berry (v2+):
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-3.6.4.cjs
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
Key advantages:
- Plugin architecture (Berry)
- Zero-installs option (Berry)
- Advanced workspace commands
- Wide ecosystem support
- Good TypeScript support
Commands:
yarn install
yarn workspace @myorg/web add react
yarn workspace @myorg/web run build
yarn workspaces foreach run build
yarn workspaces foreach --since=main run test
NPM Workspaces
Native NPM workspace support (v7+).
{
"workspaces": [
"packages/*",
"apps/*"
]
}
Key advantages:
- Native NPM support
- No additional tools needed
- Simple configuration
- Widespread compatibility
- Well-documented
Commands:
npm install
npm install react --workspace=@myorg/web
npm run build --workspace=@myorg/web
npm run build --workspaces
npm run test --workspaces --if-present
Tool Comparison and Migration
Performance comparison (1000 packages, cold install):
- PNPM: ~30 seconds
- Yarn Berry: ~45 seconds
- NPM: ~90 seconds
- Yarn Classic: ~120 seconds
Migration from NPM to PNPM:
rm -rf node_modules package-lock.json
corepack enable pnpm
cat > pnpm-workspace.yaml << EOF
packages:
- 'packages/*'
- 'apps/*'
EOF
pnpm install
Migration from Yarn to PNPM:
rm -rf node_modules yarn.lock
cat > pnpm-workspace.yaml << EOF
packages:
- 'packages/*'
- 'apps/*'
EOF
pnpm install --lockfile-only
pnpm install
Best Practices
1. Use Caching Extensively
Leverage local and remote caching for maximum speed.
Implementation:
- Enable caching for all deterministic tasks
- Configure remote cache for CI/CD
- Set up proper cache keys
- Monitor cache hit rates
- Document cache configuration
2. Configure Remote Cache for CI
Share build artifacts across CI runs and developers.
Implementation:
- Set up remote cache service (Vercel, Nx Cloud, S3)
- Configure authentication
- Enable in CI environment
- Monitor cache usage
- Implement cache warming
3. Leverage Affected Commands
Only run tasks on changed packages.
Implementation:
- Use affected analysis in CI
- Configure base branch correctly
- Test affected strategy locally
- Document affected commands
- Combine with caching
4. Optimize Task Pipelines
Design efficient task dependency graphs.
Implementation:
- Minimize task dependencies
- Enable parallel execution
- Configure appropriate concurrency
- Use pipeline visualization
- Profile pipeline performance
5. Use Code Generation for Consistency
Automate package and component creation.
Implementation:
- Create generators for common patterns
- Document generator usage
- Maintain generator templates
- Use generators in CI for validation
- Regular template updates
6. Version Lock Files in Monorepo
Commit lock files for reproducible builds.
Implementation:
- Commit root lock file
- Don't commit package lock files
- Use
--frozen-lockfile in CI
- Regular lock file updates
- Document lock file policy
7. Document Tool Usage
Maintain clear documentation for all tools.
Implementation:
- README with tool overview
- Command reference
- Configuration explanations
- Troubleshooting guide
- Migration guides
8. Monitor Build Performance
Track and optimize build times.
Implementation:
- Measure build times in CI
- Track cache hit rates
- Profile slow tasks
- Set performance budgets
- Regular performance reviews
9. Keep Tools Updated
Stay current with monorepo tool versions.
Implementation:
- Regular update schedule
- Test updates in CI
- Review changelogs
- Gradual rollout
- Rollback plan
10. Validate Workspace Integrity
Ensure workspace is correctly configured.
Implementation:
- Automated validation in CI
- Check for phantom dependencies
- Verify package relationships
- Validate versions
- Audit security
Common Pitfalls
1. Not Using Build Caching
Missing significant performance gains.
Solution: Enable local and remote caching, configure outputs correctly.
2. Poor Pipeline Configuration
Inefficient task dependencies and ordering.
Solution: Minimize dependencies, enable parallelism, visualize pipeline.
3. Running All Tasks Always
Not using affected analysis.
Solution: Implement affected commands, configure base branch, test locally.
4. Ignoring Affected Analysis
Wasting CI time on unchanged packages.
Solution: Use affected in CI, configure correctly, monitor savings.
5. Manual Package Creation
Inconsistent package structure.
Solution: Create generators, document usage, enforce in reviews.
6. Inconsistent Tool Usage
Different team members using different commands.
Solution: Document standard commands, use package.json scripts, code review.
7. Cache Invalidation Issues
Stale builds from incorrect cache configuration.
Solution: Configure inputs/outputs correctly, test cache behavior, monitor.
8. Over-Complicated Pipelines
Complex task graphs that are hard to maintain.
Solution: Simplify dependencies, regular reviews, documentation.
9. Mixing Tools Unnecessarily
Using multiple tools that overlap.
Solution: Choose one primary tool, justify additions, regular audits.
10. No Performance Monitoring
Not tracking build performance over time.
Solution: Implement metrics, regular reviews, set budgets, optimize.
When to Use This Skill
Apply monorepo tooling practices when:
- Setting up new monorepo - Choosing and configuring tools
- Optimizing build performance - Improving speed and efficiency
- Migrating between tools - Moving from one tool to another
- Configuring CI/CD - Setting up automated builds and tests
- Implementing caching - Local and remote cache setup
- Creating generators - Automating code scaffolding
- Troubleshooting builds - Solving build and dependency issues
- Scaling monorepo - Handling growth and complexity
- Team onboarding - Teaching developers monorepo workflows
- Evaluating tools - Comparing and selecting monorepo tools
Resources