Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds
Torna alle collezioni
Guide15 min read

Turborepo

Turborepo is a high-performance build system for JavaScript/TypeScript monorepos with intelligent caching, parallel execution, and remote caching by Vercel.

Turborepo - Complete Guide to High-Performance Monorepo Build System

What is Turborepo?

Turborepo is a high-performance build system designed specifically for JavaScript and TypeScript monorepos. Acquired by Vercel in 2021, it has become one of the most popular tools for managing large codebases. The core idea behind Turborepo is to speed up builds through intelligent caching, parallel execution, and incremental builds.

In a traditional approach, building a monorepo means running the same tasks repeatedly, even when the code hasn't changed. Turborepo solves this problem by remembering the results of previous builds and skipping unnecessary work. The result? Builds that used to take minutes now finish in seconds.

Why Turborepo?

Key advantages of Turborepo

  1. Intelligent caching - Doesn't rebuild what's already built
  2. Parallel execution - Utilizes all CPU cores
  3. Remote caching - Share cache between team and CI
  4. Incremental builds - Build only what changed
  5. Pipeline definition - Define dependencies between tasks
  6. Zero config - Works with npm, pnpm, yarn workspaces
  7. Vercel integration - Native Vercel integration
  8. Pruned subsets - Deploy only the packages you need

Turborepo vs Other Build Systems

FeatureTurborepoNxLernaRush
CachingLocal + RemoteLocal + RemoteNone (plugin)Local
SetupMinimalComplexSimpleComplex
Learning curveEasyMediumEasyHard
Remote cacheVercel (free)Nx CloudNoneAzure
EcosystemGrowingLargeLegacyEnterprise
Task runnerBuilt-inBuilt-innpm/yarnCustom
PriceFree + Vercel tiersFree + Cloud tiersFreeFree

When to choose Turborepo?

  • Speed is a priority - fastest time to a working build
  • Already using Vercel - native integration
  • Simple setup - minimal configuration
  • Using npm/pnpm/yarn workspaces - zero migration effort
  • Need remote cache - sharing between CI and developers

Installation and Configuration

New project

Code
Bash
# Create a new monorepo with Turborepo
npx create-turbo@latest my-monorepo

# Or with a specific template
npx create-turbo@latest my-monorepo --example with-tailwind
npx create-turbo@latest my-monorepo --example kitchen-sink

# With pnpm
pnpm dlx create-turbo@latest my-monorepo

Adding to an existing project

Code
Bash
# Install Turborepo
npm install turbo --save-dev

# Or globally
npm install turbo --global

# With pnpm
pnpm add turbo --save-dev --workspace-root

Project structure

Code
TEXT
my-monorepo/
├── apps/
│   ├── web/                    # Next.js app
│   │   ├── src/
│   │   ├── package.json
│   │   └── tsconfig.json
│   ├── docs/                   # Documentation
│   │   ├── src/
│   │   └── package.json
│   └── admin/                  # Admin panel
│       └── package.json
├── packages/
│   ├── ui/                     # Shared UI components
│   │   ├── src/
│   │   ├── package.json
│   │   └── tsconfig.json
│   ├── config-eslint/          # Shared ESLint config
│   │   └── package.json
│   ├── config-typescript/      # Shared TS config
│   │   └── package.json
│   └── utils/                  # Shared utilities
│       ├── src/
│       └── package.json
├── package.json                # Root package.json
├── turbo.json                  # Turborepo config
├── pnpm-workspace.yaml         # Workspace config (pnpm)
└── .gitignore

Root package.json

Code
JSON
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "build": "turbo build",
    "dev": "turbo dev",
    "lint": "turbo lint",
    "test": "turbo test",
    "clean": "turbo clean",
    "format": "prettier --write \"**/*.{ts,tsx,md}\""
  },
  "devDependencies": {
    "turbo": "^2.0.0",
    "prettier": "^3.0.0"
  },
  "packageManager": "pnpm@8.15.0"
}

turbo.json - Main configuration

Code
JSON
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [".env"],
  "globalEnv": ["NODE_ENV", "CI"],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"],
      "env": ["DATABASE_URL", "API_KEY"]
    },
    "lint": {
      "dependsOn": ["^lint"],
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "inputs": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts"]
    },
    "clean": {
      "cache": false
    }
  }
}

Pipelines and Tasks

Understanding the Pipeline

The pipeline defines relationships between tasks in a monorepo:

Code
JSON
{
  "tasks": {
    "build": {
      // ^ means "build dependencies first"
      "dependsOn": ["^build"],
      // Output files to cache
      "outputs": ["dist/**", ".next/**"]
    },
    "test": {
      // Depends on local build (without ^)
      "dependsOn": ["build"],
      // Input files that affect the cache
      "inputs": ["src/**", "test/**"]
    },
    "deploy": {
      // Depends on build and test of the same package
      "dependsOn": ["build", "test"]
    }
  }
}

Dependency types

Code
JSON
{
  "tasks": {
    "build": {
      // ^build - build dependencies first (topological)
      "dependsOn": ["^build"]
    },
    "test": {
      // build - build THIS package first (same package)
      "dependsOn": ["build"]
    },
    "deploy": {
      // Both types together
      "dependsOn": ["^build", "test", "lint"]
    },
    "e2e": {
      // Dependency on a specific package
      "dependsOn": ["web#build"]
    }
  }
}

Outputs and Caching

Code
JSON
{
  "tasks": {
    "build": {
      "outputs": [
        "dist/**",
        ".next/**",
        "!.next/cache/**",  // Exclude cache
        "build/**"
      ]
    },
    "lint": {
      // No outputs = task doesn't produce files
      "outputs": []
    },
    "test": {
      "outputs": ["coverage/**"]
    }
  }
}

Inputs - controlling cache invalidation

Code
JSON
{
  "tasks": {
    "build": {
      // Only these files affect the cache
      "inputs": [
        "src/**/*.ts",
        "src/**/*.tsx",
        "package.json",
        "tsconfig.json"
      ]
    },
    "lint": {
      "inputs": [
        "src/**/*.ts",
        "src/**/*.tsx",
        ".eslintrc.js",
        "package.json"
      ]
    },
    "test": {
      "inputs": [
        "$TURBO_DEFAULT$",  // Default inputs
        "jest.config.js",
        "test/**"
      ]
    }
  }
}

Environment Variables

Code
JSON
{
  // Global env - affects all tasks
  "globalEnv": ["CI", "NODE_ENV"],

  // Global dependencies - files affecting everything
  "globalDependencies": [".env", "tsconfig.base.json"],

  "tasks": {
    "build": {
      // Task-specific env
      "env": ["DATABASE_URL", "API_KEY", "NEXT_PUBLIC_*"],
      "passThroughEnv": ["AWS_SECRET_KEY"]  // Doesn't affect cache
    }
  }
}

Running Tasks

Basic commands

Code
Bash
# Run task in all packages
turbo build
turbo lint
turbo test

# Dev mode (no cache, persistent)
turbo dev

# Verbose output
turbo build --verbosity=2

# Dry run - show what will be executed
turbo build --dry-run

# Show dependency graph
turbo build --graph
turbo build --graph=graph.png

Filtering packages

Code
Bash
# Only a specific package
turbo build --filter=web
turbo build --filter=@repo/ui

# Package and its dependencies
turbo build --filter=web...

# Package and its dependents (packages that depend on it)
turbo build --filter=...@repo/ui

# Packages in a specific folder
turbo build --filter="./apps/*"
turbo build --filter="./packages/*"

# Exclude packages
turbo build --filter="!docs"

# Combinations
turbo build --filter="web..." --filter="!docs"

# Only changed since last commit
turbo build --filter="[HEAD^1]"
turbo build --filter="...[main...HEAD]"

Advanced filters

Code
Bash
# Packages changed in PR
turbo build --filter="[origin/main...HEAD]"

# Packages depending on changed ui
turbo build --filter="...@repo/ui[HEAD^1]"

# All apps
turbo build --filter="./apps/**"

# Packages with a specific tag in package.json
turbo build --filter="@repo/*"

Execution options

Code
Bash
# Limit parallel tasks
turbo build --concurrency=4
turbo build --concurrency=50%  # 50% CPU cores

# Continue despite errors
turbo build --continue

# Force rebuild (ignore cache)
turbo build --force

# Output logs
turbo build --output-logs=full
turbo build --output-logs=hash-only
turbo build --output-logs=new-only
turbo build --output-logs=errors-only

Remote Caching

Configuration with Vercel

Code
Bash
# Login to Vercel
npx turbo login

# Link your project
npx turbo link

# Now cache is shared!
turbo build

Verifying Remote Cache

Code
Bash
# Check status
turbo build --summarize

# Output will show:
# Cache: 5 cached, 2 computed
# Remote: 3 downloaded, 2 uploaded

Self-hosted Remote Cache

Code
TypeScript
// Custom cache server (Express example)
import express from 'express'
import { createHash } from 'crypto'

const app = express()
const cache = new Map<string, Buffer>()

// GET artifact
app.get('/v8/artifacts/:hash', (req, res) => {
  const artifact = cache.get(req.params.hash)
  if (artifact) {
    res.send(artifact)
  } else {
    res.status(404).send('Not found')
  }
})

// PUT artifact
app.put('/v8/artifacts/:hash', express.raw({ limit: '50mb' }), (req, res) => {
  cache.set(req.params.hash, req.body)
  res.status(200).send('OK')
})

app.listen(3001)
Code
Bash
# Using a custom server
turbo build --api="http://localhost:3001" --token="secret"

CI/CD Configuration

.github/workflows/ci.yml
YAML
# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v2
        with:
          version: 8

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - run: pnpm install

      - run: pnpm build
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ vars.TURBO_TEAM }}

Workspace Packages

UI component package

packages/ui/package.json
JSON
// packages/ui/package.json
{
  "name": "@repo/ui",
  "version": "0.0.0",
  "private": true,
  "exports": {
    ".": "./src/index.ts",
    "./button": "./src/button.tsx",
    "./card": "./src/card.tsx",
    "./styles.css": "./styles.css"
  },
  "scripts": {
    "build": "tsup src/index.ts --format cjs,esm --dts",
    "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
    "lint": "eslint src/",
    "test": "vitest run"
  },
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "@repo/config-typescript": "workspace:*",
    "tsup": "^8.0.0",
    "typescript": "^5.0.0"
  }
}
TSpackages/ui/src/index.ts
TypeScript
// packages/ui/src/index.ts
export { Button } from './button'
export { Card } from './card'
export { Input } from './input'
export type { ButtonProps, CardProps, InputProps } from './types'

Shared Config Packages

packages/config-eslint/package.json
JSON
// packages/config-eslint/package.json
{
  "name": "@repo/config-eslint",
  "version": "0.0.0",
  "private": true,
  "main": "index.js",
  "dependencies": {
    "@typescript-eslint/eslint-plugin": "^7.0.0",
    "@typescript-eslint/parser": "^7.0.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-react": "^7.0.0",
    "eslint-plugin-react-hooks": "^4.0.0"
  }
}
JSpackages/config-eslint/index.js
JavaScript
// packages/config-eslint/index.js
module.exports = {
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'prettier'
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'react'],
  rules: {
    'react/react-in-jsx-scope': 'off',
    '@typescript-eslint/no-unused-vars': 'warn'
  },
  settings: {
    react: { version: 'detect' }
  }
}

Using shared packages

apps/web/package.json
JSON
// apps/web/package.json
{
  "name": "web",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "lint": "eslint . --ext .ts,.tsx"
  },
  "dependencies": {
    "@repo/ui": "workspace:*",
    "@repo/utils": "workspace:*",
    "next": "^14.0.0",
    "react": "^18.0.0"
  },
  "devDependencies": {
    "@repo/config-eslint": "workspace:*",
    "@repo/config-typescript": "workspace:*"
  }
}
JSapps/web/.eslintrc.js
JavaScript
// apps/web/.eslintrc.js
module.exports = {
  extends: ['@repo/config-eslint'],
  parserOptions: {
    project: './tsconfig.json'
  }
}

TypeScript Config Sharing

packages/config-typescript/base.json
JSON
// packages/config-typescript/base.json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true
  }
}
packages/config-typescript/nextjs.json
JSON
// packages/config-typescript/nextjs.json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": "./base.json",
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "ES2022"],
    "module": "ESNext",
    "target": "ES2022",
    "jsx": "preserve",
    "plugins": [{ "name": "next" }]
  }
}
apps/web/tsconfig.json
JSON
// apps/web/tsconfig.json
{
  "extends": "@repo/config-typescript/nextjs.json",
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

Package-specific Tasks

Overriding tasks per-package

turbo.json
JSON
// turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    // Override for a specific package
    "web#build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**"],
      "env": ["NEXT_PUBLIC_API_URL"]
    },
    // Override for docs
    "docs#build": {
      "dependsOn": ["^build"],
      "outputs": [".docusaurus/**", "build/**"]
    }
  }
}

Local turbo.json in a package

apps/web/turbo.json
JSON
// apps/web/turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "extends": ["//"],  // Inherit from root turbo.json
  "tasks": {
    "build": {
      // Override only specific options
      "env": ["ANALYZE", "NEXT_PUBLIC_SENTRY_DSN"]
    },
    "dev": {
      "persistent": true,
      "cache": false
    }
  }
}

Generators (Codegen)

Creating a generator

Code
Bash
# Create generators folder
mkdir -p turbo/generators
TSturbo/generators/config.ts
TypeScript
// turbo/generators/config.ts
import type { PlopTypes } from '@turbo/gen'

export default function generator(plop: PlopTypes.NodePlopAPI): void {
  // New package generator
  plop.setGenerator('package', {
    description: 'Create a new package',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Package name:'
      },
      {
        type: 'list',
        name: 'type',
        message: 'Package type:',
        choices: ['lib', 'config', 'tool']
      }
    ],
    actions: [
      {
        type: 'add',
        path: 'packages/{{name}}/package.json',
        templateFile: 'templates/package.json.hbs'
      },
      {
        type: 'add',
        path: 'packages/{{name}}/src/index.ts',
        templateFile: 'templates/index.ts.hbs'
      },
      {
        type: 'add',
        path: 'packages/{{name}}/tsconfig.json',
        templateFile: 'templates/tsconfig.json.hbs'
      }
    ]
  })

  // New app generator
  plop.setGenerator('app', {
    description: 'Create a new application',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Application name:'
      },
      {
        type: 'list',
        name: 'framework',
        message: 'Framework:',
        choices: ['next', 'remix', 'astro']
      }
    ],
    actions: (data) => {
      const actions: PlopTypes.ActionType[] = []

      if (data?.framework === 'next') {
        actions.push({
          type: 'addMany',
          destination: 'apps/{{name}}',
          templateFiles: 'templates/next/**/*',
          base: 'templates/next'
        })
      }

      return actions
    }
  })
}

Using the generator

Code
Bash
# List available generators
turbo gen

# Run a specific generator
turbo gen package
turbo gen app

# With arguments
turbo gen package --name=my-lib --type=lib

Templates

Code
HANDLEBARS
{{!-- turbo/generators/templates/package.json.hbs --}}
{
  "name": "@repo/{{name}}",
  "version": "0.0.0",
  "private": true,
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  "scripts": {
    "build": "tsup src/index.ts --format cjs,esm --dts",
    "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
    "lint": "eslint src/",
    "test": "vitest run"
  },
  "devDependencies": {
    "@repo/config-typescript": "workspace:*",
    "tsup": "^8.0.0",
    "typescript": "^5.0.0"
  }
}

Pruned Deployments

Docker with Pruned Monorepo

Code
DOCKERFILE
# Dockerfile
FROM node:20-alpine AS base

# Pruner stage
FROM base AS pruner
RUN npm install -g turbo
WORKDIR /app
COPY . .
RUN turbo prune web --docker

# Installer stage
FROM base AS installer
WORKDIR /app

# Install only needed dependencies
COPY --from=pruner /app/out/json/ .
COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable pnpm && pnpm install --frozen-lockfile

# Builder stage
FROM base AS builder
WORKDIR /app
COPY --from=installer /app/ .
COPY --from=pruner /app/out/full/ .

RUN corepack enable pnpm && pnpm turbo build --filter=web

# Runner stage
FROM base AS runner
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs

COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public

CMD ["node", "apps/web/server.js"]

Pruning for CI

Code
Bash
# Create pruned subset
turbo prune web --docker

# Result in out/:
# out/
# ├── json/           # package.json files
# ├── full/           # Full source code
# └── pnpm-lock.yaml  # Pruned lockfile

Debugging and Troubleshooting

Cache Debugging

Code
Bash
# Show why a task was executed
turbo build --summarize

# Show hash computation
turbo build --dry-run=json | jq

# Force cache miss
turbo build --force

# Clear cache
turbo clean
rm -rf node_modules/.cache/turbo

Task Graph

Code
Bash
# Generate dependency graph
turbo build --graph=graph.html
turbo build --graph=graph.png
turbo build --graph=graph.json

# Show in console
turbo build --graph

Verbose Logging

Code
Bash
# Verbosity levels
turbo build --verbosity=0  # Quiet
turbo build --verbosity=1  # Default
turbo build --verbosity=2  # Verbose

# Show all logs
turbo build --output-logs=full

# Only errors
turbo build --output-logs=errors-only

Common Issues

Code
Bash
# Problem: Cache not working
# Solution: Check inputs and outputs

# Problem: Task runs despite no changes
# Check env variables
turbo build --summarize | grep -A 10 "environment"

# Problem: Circular dependency
turbo build --graph  # Visualize dependencies

# Problem: Slow builds
turbo build --profile=profile.json
# Analyze in Chrome DevTools

Best Practices

Package structure

Code
TEXT
packages/
├── core/            # Business logic (no UI)
├── ui/              # Shared React components
├── hooks/           # Shared React hooks
├── utils/           # Helpers, formatters
├── types/           # Shared TypeScript types
├── config-*/        # Shared configs (eslint, ts, prettier)
└── api-client/      # Generated API client

Naming Conventions

Code
JSON
// Use scope for packages
{
  "name": "@repo/ui",        // ✅
  "name": "@mycompany/ui",   // ✅
  "name": "ui",              // ❌ Conflicts with npm packages
}

Dependency Management

Code
JSON
// Root package.json - shared devDependencies
{
  "devDependencies": {
    "turbo": "^2.0.0",
    "typescript": "^5.0.0",
    "prettier": "^3.0.0"
  }
}

// Package - only specific deps
{
  "dependencies": {
    "@repo/ui": "workspace:*"  // Internal dependency
  },
  "devDependencies": {
    "@repo/config-eslint": "workspace:*"
  }
}

Task Organization

Code
JSON
{
  "tasks": {
    // Build pipeline
    "build": { "dependsOn": ["^build"] },
    "build:production": { "dependsOn": ["^build", "lint", "test"] },

    // Dev
    "dev": { "cache": false, "persistent": true },

    // Quality
    "lint": { "outputs": [] },
    "lint:fix": { "outputs": [], "cache": false },
    "typecheck": { "outputs": [] },
    "test": { "dependsOn": ["build"] },
    "test:watch": { "cache": false, "persistent": true },

    // Maintenance
    "clean": { "cache": false }
  }
}

Integrations

Vercel Deployment

vercel.json
JSON
// vercel.json
{
  "buildCommand": "pnpm turbo build --filter=web",
  "installCommand": "pnpm install",
  "framework": "nextjs",
  "outputDirectory": "apps/web/.next"
}

GitHub Actions

.github/workflows/ci.yml
YAML
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ vars.TURBO_TEAM }}

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2  # Needed for --filter=[HEAD^1]

      - uses: pnpm/action-setup@v2
        with:
          version: 8

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install

      - name: Build
        run: pnpm turbo build

      - name: Test
        run: pnpm turbo test

      - name: Lint
        run: pnpm turbo lint

Changesets (versioning)

Code
Bash
npm install @changesets/cli -D
npx changeset init
.changeset/config.json
JSON
// .changeset/config.json
{
  "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "restricted",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": ["@repo/config-*"]
}

FAQ - Frequently Asked Questions

Does Turborepo require Vercel?

No! Turborepo is open-source and works without Vercel. Remote caching can be self-hosted or you can use Vercel as a convenient option.

How do I migrate from Lerna/Nx?

Turborepo is additive - you can add turbo.json to an existing monorepo without removing other tools. Gradually move tasks to Turborepo.

Can I use npm instead of pnpm?

Yes! Turborepo works with npm workspaces, pnpm workspaces, and yarn workspaces. pnpm is recommended for performance reasons.

How do I debug caching issues?

Use turbo build --summarize to see what affects the cache hash. Check env variables, inputs, and outputs in turbo.json.

Does Turborepo support monorepos with different languages?

Turborepo is optimized for JavaScript/TypeScript but can orchestrate any tasks. For polyglot monorepos, consider Nx or Bazel.

What's the difference between dependsOn: ["^build"] and dependsOn: ["build"]?

  • ^build - build dependencies first (packages we depend on)
  • build - build in the same package first

How to optimize CI build time?

  1. Enable remote caching
  2. Use --filter for changed packages
  3. Parallel jobs in CI
  4. Optimize inputs/outputs

Summary

Turborepo is a powerful tool for managing monorepos that offers:

  • Intelligent caching - local and remote
  • Parallel execution - maximum CPU utilization
  • Minimal config - works out-of-the-box
  • Vercel integration - native integration
  • Incremental adoption - easy migration

Ideal for teams looking for a simple yet powerful monorepo solution without unnecessary complexity.