Monorepo Management Strategies with Turborepo and pnpm: A Production-Grade Implementation Guide

Use pnpm workspaces for package linking and Turborepo for task orchestration, caching, and affected-package detection to build scalable, maintainable JavaScript monorepos.

Modern JavaScript/TypeScript monorepos require disciplined tooling to manage dependencies, tasks, and versioning at scale. The Monorepo Navigator skill in the alirezarezvani/claude-skills repository provides a complete reference implementation demonstrating effective monorepo management strategies with Turborepo and pnpm. This guide extracts the architectural patterns, configuration files, and CLI workflows directly from the source code to help you implement a low-maintenance, high-performance monorepo.

Defining the Workspace Structure

Every pnpm-based monorepo starts with a root-level workspace definition. In the alirezarezvani/claude-skills implementation, the pnpm-workspace.yaml file declares three distinct directory globs that organize code by responsibility.


# pnpm-workspace.yaml

packages:
  - "apps/*"
  - "packages/*"
  - "tools/*"

This structure separates deployable applications (apps), shared libraries (packages), and internal tooling (tools). According to the documentation in engineering/monorepo-navigator/SKILL.md, this convention ensures that pnpm treats every subdirectory within these paths as an individual package while maintaining a single root lockfile.

Configuring the Turborepo Pipeline

Turborepo orchestrates tasks across the workspace using a directed acyclic graph (DAG) defined in turbo.json. The configuration in this repository demonstrates how to declare dependencies between tasks and enable aggressive caching.

{
  "$schema": "https://turbo.build/schema.json",
  "globalEnv": ["NODE_ENV", "DATABASE_URL"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**", "build/**"]
    },
    "test": {
      "dependsOn": ["^build"],
      "outputs": ["coverage/**"],
      "cache": true
    },
    "lint": {
      "outputs": [],
      "cache": true
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

The dependsOn array uses the ^ prefix to indicate that a task depends on the same task in its dependencies (e.g., test waits for upstream packages to finish build). As documented in engineering/monorepo-navigator/SKILL.md, the globalEnv array ensures that changes to environment variables invalidate the cache appropriately.

Linking Packages with the Workspace Protocol

Cross-package dependencies resolve via the workspace protocol, guaranteeing that imports always point to the current source tree rather than published registry versions.

// apps/web/package.json
{
  "name": "@myorg/web",
  "dependencies": {
    "@myorg/ui": "workspace:*",
    "@myorg/utils": "workspace:^"
  }
}

The workspace:* protocol pins the dependency to the exact version in the monorepo, while workspace:^ allows caret-range semantics. This approach, detailed in the skill documentation, eliminates version drift during development and ensures that changes in packages/ui immediately reflect in apps/web without publishing.

Executing Tasks and Affected Package Detection

Turborepo’s selective execution filters minimize CI time by running tasks only on packages that changed since the last commit. The repository demonstrates two primary filter patterns:


# Build only packages affected by the last commit

turbo run build --filter=...[HEAD^1]

# Build a specific package and all its dependencies

turbo run build --filter=@myorg/web...

The ...[HEAD^1] syntax calculates the minimal subgraph requiring rebuild by comparing the current commit against its parent. For CI pipelines targeting pull requests, the repository uses --filter=...[origin/main] to detect changes relative to the main branch, as shown in docs/skills/engineering/monorepo-navigator.md.

To preview execution without running commands, use the dry-run flag:

turbo run build --dry-run

Remote Caching for CI Acceleration

The alirezarezvani/claude-skills repository emphasizes remote caching to share task outputs across CI runners and local machines. By storing compiled assets and test results in a remote cache keyed by input hashes, teams avoid redundant computation.

Enable Vercel Remote Cache with:

turbo login
turbo link

Alternatively, self-hosted cache servers provide the same functionality for private infrastructure. The configuration in .github/workflows/ci-quality-gate.yml demonstrates passing authentication tokens via environment variables:

- name: Build affected
  run: turbo run build --filter=...[origin/main]
  env:
    TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
    TURBO_TEAM: ${{ vars.TURBO_TEAM }}

Versioning and Publishing with Changesets

Manual version bumps become unmanageable in large monorepos. The reference implementation uses Changesets to automate changelog generation, version bumping, and publishing.


# Create a changeset after modifying packages

pnpm changeset

# Version bump and publish in CI

pnpm changeset version
pnpm changeset publish

As documented in docs/skills/engineering/monorepo-navigator.md, this workflow replaces manual edits to package.json files, ensures deterministic publishing order based on the dependency graph, and supports pre-release channels for beta testing.

Documentation and Discovery Standards

The repository establishes documentation conventions that scale with the codebase. A root-level CLAUDE.md maps the overall monorepo structure, while individual packages contain scoped CLAUDE.md files enforcing specific testing rules and command patterns. This approach, referenced in engineering/monorepo-navigator/SKILL.md, enables AI assistants and new developers to navigate the codebase without deep institutional knowledge.

Summary

  • pnpm workspaces define the monorepo boundary via pnpm-workspace.yaml, organizing code into apps, packages, and tools directories.
  • Turborepo pipelines in turbo.json declare task dependencies and caching rules, enabling parallel execution and output reuse.
  • The workspace protocol (workspace:*) ensures cross-package dependencies resolve to local source, eliminating version conflicts during development.
  • Affected detection via --filter=...[HEAD^1] or --filter=...[origin/main] minimizes CI execution time by targeting only changed packages.
  • Remote caching (Vercel or self-hosted) shares task outputs across machines, dramatically reducing build and test durations.
  • Changesets automate versioning and publishing workflows, replacing manual package bumps with structured change documentation.

Frequently Asked Questions

How does pnpm differ from npm or Yarn in a monorepo context?

pnpm uses a content-addressable store and hard links to share dependencies across packages, consuming significantly less disk space than npm or Yarn Classic. Its workspace implementation requires explicit pnpm-workspace.yaml configuration and supports the workspace: protocol natively, providing stricter control over internal dependency resolution than Yarn's link: protocol or npm workspaces.

What is the purpose of the dependsOn configuration in turbo.json?

The dependsOn array defines the task dependency graph that Turborepo uses to schedule execution. The ^ prefix indicates a topological dependency—meaning a package's build task waits for its dependencies' build tasks to complete. This ensures that downstream packages never consume stale artifacts from upstream changes.

How do you determine which packages need rebuilding in CI?

Use the --filter flag with a Git reference range. The syntax --filter=...[origin/main] tells Turborepo to select packages that changed between the current commit and the main branch, plus their dependents. This calculated subgraph represents the minimal set requiring validation, reducing CI costs and execution time.

Can Turborepo work with other package managers like npm or Yarn?

Yes, Turborepo is package-manager agnostic and works with npm, Yarn, and pnpm. However, the alirezarezvani/claude-skills repository specifically recommends pnpm for its strict workspace isolation, efficient storage, and native support for the workspace: protocol, which simplifies internal dependency management compared to npm or Yarn implementations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →