Rule Writing

CLAUDE.md for Turborepo Monorepos

AI generates single-repo patterns in monorepos — ignoring task pipelines, caching, and workspace dependencies. Rules for turbo.json, internal packages, and remote caching.

7 min read·January 7, 2025

Turborepo builds only what changed — AI rebuilds everything every time

Task pipelines, intelligent caching, internal packages, remote cache, and CI optimization

Why Turborepo Needs Pipeline and Caching Rules

Turborepo is a build system for JavaScript/TypeScript monorepos — it orchestrates tasks across packages with dependency-aware scheduling, intelligent caching, and remote cache sharing. AI assistants treat monorepos as collections of independent projects: running npm build in each package sequentially, ignoring the task pipeline, and rebuilding everything on every change instead of using the cache.

Turborepo's value is: build only what changed, cache everything else, and run tasks in the optimal order based on dependencies. AI code that bypasses the pipeline and cache throws away all three benefits — you get the complexity of a monorepo without the speed advantage.

These rules target Turborepo 2+ with pnpm workspaces (the most common setup). They cover turbo.json configuration, internal packages, remote caching, and CI optimization.

Rule 1: turbo.json Task Pipeline

The rule: 'Define all tasks in turbo.json: { "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**"] }, "test": { "dependsOn": ["build"] }, "lint": {}, "dev": { "cache": false, "persistent": true } } }. dependsOn: ["^build"] means: build this package's dependencies first. outputs: defines what to cache. cache: false for dev servers (never cached). persistent: true for long-running tasks.'

For dependency ordering: the ^ prefix means upstream dependency: ^build builds all workspace dependencies before this package. No prefix means task-level: lint depends on nothing and runs immediately. Use dependsOn to express the correct build order — Turborepo parallelizes everything that has no dependency.

AI runs turbo build without configuring turbo.json — every task runs sequentially with no caching. One configuration file transforms the build: parallel execution, dependency-aware ordering, and cached outputs that skip unchanged packages.

  • turbo.json: define every task — build, test, lint, dev, typecheck
  • dependsOn: ['^build'] — build dependencies first, then this package
  • outputs: ['dist/**'] — what to cache for this task
  • cache: false for dev — persistent: true for long-running (dev server)
  • Turborepo parallelizes tasks without dependencies — maximizes CPU usage
💡 One Config, Total Transformation

turbo.json with dependsOn + outputs transforms your build: parallel execution, dependency-aware ordering, cached outputs. Without it, Turborepo runs tasks sequentially with no caching — same as running npm build manually.

Rule 2: Internal Packages for Shared Code

The rule: 'Use internal packages for shared code: packages/ui (shared components), packages/config (shared tsconfig, eslint), packages/database (shared schema + client), packages/types (shared TypeScript types). Each package has its own package.json with a name: @repo/ui, @repo/config. Apps import from internal packages: import { Button } from "@repo/ui".'

For internal package structure: 'Each package: package.json (name, exports, dependencies), tsconfig.json (extends from @repo/config), src/ (source code), and optionally dist/ (compiled output). Use the exports field for clean imports: "exports": { ".": "./src/index.ts" }. No build step needed for TypeScript internal packages if your bundler handles .ts imports (Next.js, Vite, esbuild do).'

AI puts shared code in a shared/ directory without proper package.json — it works locally but breaks: no dependency tracking (Turborepo doesn't know about it), no caching (can't hash inputs for shared/), and no exports (imports are brittle paths). Internal packages give shared code first-class monorepo citizenship.

⚠️ No Shared Directory

AI puts shared code in a shared/ directory without package.json. Turborepo can't track it (no dependency graph), can't cache it (no defined outputs), and imports are brittle paths. Internal packages with proper package.json give shared code first-class monorepo citizenship.

Rule 3: Remote Caching for Team Speed

The rule: 'Enable remote caching for team-wide build sharing: npx turbo login && npx turbo link. When developer A builds a package, the output is cached remotely. When developer B builds the same package (same inputs), Turbo downloads the cached output — no rebuild. This turns 10-minute builds into 10-second cache restores across the entire team and CI.'

For Vercel Remote Cache: 'Turborepo's remote cache is built into Vercel — login with your Vercel account. For self-hosted: use Turborepo's custom remote cache server (turbo-remote-cache on npm) with S3 or R2 backend. Set TURBO_TOKEN and TURBO_TEAM in CI environment variables.'

For cache inputs: 'Turborepo hashes task inputs to determine cache keys: source files, dependencies, environment variables (configured in turbo.json globalEnv/env). Add environment variables that affect the build: "globalEnv": ["NODE_ENV"], "tasks": { "build": { "env": ["API_URL"] } }. Missed env vars = stale cache — the most common remote cache bug.'

  • Remote cache: build once, share across team + CI — 10min → 10sec
  • Vercel Remote Cache: built-in — npx turbo login/link to enable
  • Self-hosted: turbo-remote-cache with S3/R2 backend
  • globalEnv for all tasks — env per task — missed env = stale cache
  • TURBO_TOKEN + TURBO_TEAM in CI for remote cache access
10min → 10sec

Remote cache means: developer A builds @repo/ui, the output is cached. Developer B (same inputs) downloads the cached output — no rebuild. Across a team, this turns 10-minute builds into 10-second cache restores.

Rule 4: Workspace Dependencies and Package Management

The rule: 'Use pnpm workspaces (recommended) or npm/yarn workspaces. Define in pnpm-workspace.yaml: packages: ["apps/*", "packages/*"]. Reference internal packages as dependencies: "@repo/ui": "workspace:*". Use workspace:* protocol — pnpm resolves to the local package. Never use file: or link: — they don't work consistently across tools.'

For dependency management: 'Install shared dev dependencies at the root: pnpm add -Dw typescript eslint. Install package-specific dependencies in the package: cd packages/ui && pnpm add react. Use catalog: protocol (pnpm 9.5+) for version alignment: pnpm catalog set react 19.0.0, then in package.json: "react": "catalog:".'

For hoisting: 'pnpm's default strict mode prevents phantom dependencies — a package can only import what it declares in its package.json. This catches missing dependencies that would pass with npm/yarn hoisting. Keep strict mode — it prevents production crashes from undeclared dependencies.'

Rule 5: CI Optimization

Use turbo run build test lint --filter=...[HEAD~1] to run only tasks affected by the last commit. Use --filter=[app-name]... to run tasks for a specific app and its dependencies. In CI: turbo run build test lint --cache-dir=.turbo (local cache) + TURBO_TOKEN for remote cache. This reduces CI time from rebuilding everything to rebuilding only what changed.

For GitHub Actions: cache node_modules and .turbo across runs with actions/cache keyed on the lockfile hash. Use pnpm/action-setup for pnpm installation. Run: pnpm turbo run build test lint. The combination of pnpm cache + Turbo remote cache means CI only downloads and builds what is new.

For Docker: 'Use turbo prune for Docker builds: npx turbo prune @repo/web --docker. This creates a pruned monorepo with only the packages needed for one app — much smaller Docker context. Use multi-stage build: copy pruned lockfile → install deps → copy pruned source → build → runtime image.'

  • --filter=...[HEAD~1] — only affected tasks since last commit
  • --filter=[app-name]... — specific app + its dependencies
  • Remote cache in CI: TURBO_TOKEN + TURBO_TEAM env vars
  • turbo prune for Docker: pruned monorepo per app — smaller images
  • pnpm cache + Turbo remote cache = minimal CI work on every commit

Complete Turborepo Rules Template

Consolidated rules for Turborepo monorepo projects.

  • turbo.json: define every task — dependsOn for ordering, outputs for caching
  • Internal packages: @repo/ui, @repo/config — proper package.json + exports
  • Remote caching: turbo login/link — TURBO_TOKEN in CI — 10min → 10sec
  • globalEnv + task env in turbo.json — missed env vars = stale cache
  • pnpm workspaces: workspace:* protocol — strict mode prevents phantom deps
  • --filter for affected tasks — turbo prune for Docker — CI optimization
  • Root dev deps: typescript, eslint — package deps in each package.json
  • catalog: protocol for version alignment — pnpm-workspace.yaml for workspace config