monorepo-management
Use this skill when setting up or managing monorepos, configuring workspace dependencies, optimizing build caching, or choosing between monorepo tools. Triggers on Turborepo, Nx, Bazel, pnpm workspaces, npm workspaces, yarn workspaces, build pipelines, task orchestration, affected commands, and any task requiring multi-package repository management.
engineering monorepoturboreponxworkspacesbuild-cachingtoolingWhat is monorepo-management?
Use this skill when setting up or managing monorepos, configuring workspace dependencies, optimizing build caching, or choosing between monorepo tools. Triggers on Turborepo, Nx, Bazel, pnpm workspaces, npm workspaces, yarn workspaces, build pipelines, task orchestration, affected commands, and any task requiring multi-package repository management.
monorepo-management
monorepo-management is a production-ready AI agent skill for claude-code, gemini-cli, openai-codex. Setting up or managing monorepos, configuring workspace dependencies, optimizing build caching, or choosing between monorepo tools.
Quick Facts
| Field | Value |
|---|---|
| Category | engineering |
| Version | 0.1.0 |
| Platforms | claude-code, gemini-cli, openai-codex |
| License | MIT |
How to Install
- Make sure you have Node.js installed on your machine.
- Run the following command in your terminal:
npx skills add AbsolutelySkilled/AbsolutelySkilled --skill monorepo-management- The monorepo-management skill is now available in your AI coding agent (Claude Code, Gemini CLI, OpenAI Codex, etc.).
Overview
A monorepo is a single version-controlled repository that houses multiple packages or applications sharing common tooling, dependencies, and build infrastructure. Done well, a monorepo eliminates dependency drift between packages, enables atomic cross-package changes, and lets you run only the builds and tests affected by a given change. This skill covers workspace managers (pnpm, npm, yarn), task orchestrators (Turborepo, Nx), enterprise build systems (Bazel), internal package patterns, and shared tooling config.
Tags
monorepo turborepo nx workspaces build-caching tooling
Platforms
- claude-code
- gemini-cli
- openai-codex
Related Skills
Pair monorepo-management with these complementary skills:
Frequently Asked Questions
What is monorepo-management?
Use this skill when setting up or managing monorepos, configuring workspace dependencies, optimizing build caching, or choosing between monorepo tools. Triggers on Turborepo, Nx, Bazel, pnpm workspaces, npm workspaces, yarn workspaces, build pipelines, task orchestration, affected commands, and any task requiring multi-package repository management.
How do I install monorepo-management?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill monorepo-management in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support monorepo-management?
This skill works with claude-code, gemini-cli, openai-codex. Install it once and use it across any supported AI coding agent.
Maintainers
Generated from AbsolutelySkilled
SKILL.md
Monorepo Management
A monorepo is a single version-controlled repository that houses multiple packages or applications sharing common tooling, dependencies, and build infrastructure. Done well, a monorepo eliminates dependency drift between packages, enables atomic cross-package changes, and lets you run only the builds and tests affected by a given change. This skill covers workspace managers (pnpm, npm, yarn), task orchestrators (Turborepo, Nx), enterprise build systems (Bazel), internal package patterns, and shared tooling config.
When to use this skill
Trigger this skill when the user:
- Wants to set up a new monorepo or migrate from a multi-repo setup
- Asks how to configure Turborepo pipelines, caching, or remote caching
- Asks how to use Nx projects, affected commands, or the Nx task graph
- Needs to share TypeScript, ESLint, or Prettier configs across packages
- Asks about pnpm/npm/yarn workspace protocols and dependency hoisting
- Wants to implement internal packages with proper bundling and type exports
- Needs to choose between Turborepo, Nx, Bazel, or Lerna
- Asks about build caching, cache invalidation, or remote cache setup
Do NOT trigger this skill for:
- Single-package repository build tooling (Vite, webpack, esbuild) - use the frontend or backend skill
- Docker/container orchestration even when containers come from a monorepo
Key principles
Single source of truth - Each config (TypeScript base, ESLint rules, Prettier) lives in exactly one package and is extended everywhere else. Duplication is the root cause of config drift.
Explicit dependencies - Every package declares its workspace dependencies with
workspace:*. Never rely on hoisting to make an undeclared dependency available at runtime.Cache everything - Every deterministic task should be cached. Define precise
inputsandoutputsso the cache is never stale and never over-broad. Remote caching multiplies this benefit across CI and team.Affected-only builds - On CI, build and test only the packages that changed (directly or transitively). Running the full build on every PR does not scale past ~20 packages.
Consistent tooling - Use the same package manager, Node version, and task runner across all packages. Mixed tooling creates invisible resolution differences and breaks cache hits.
Core concepts
Workspace protocols
| Protocol | Package manager | Meaning |
|---|---|---|
workspace:* |
pnpm | Any version from workspace, keep * in lockfile |
workspace:^ |
pnpm | Resolve range but pin a semver range |
* |
yarn berry | Any version, resolved from workspace |
file:../pkg |
npm | Path reference (no lockfile version management) |
Task graph
Turborepo and Nx model tasks as a DAG. A build task with
dependsOn: ["^build"] means all dependency packages must complete their
build before the current package starts. This replaces manual ordering scripts.
Remote caching
Remote caches (Vercel, Nx Cloud, S3/GCS) store task outputs keyed by a hash of inputs. Any machine with the same inputs gets a cache hit and downloads outputs instead of recomputing. This can reduce CI time by 80-90%.
Affected analysis
Given a diff from a base branch, affected analysis walks the dependency graph in
reverse to find every package that transitively depends on a changed package.
Turborepo: --filter=...[HEAD^1]. Nx: nx affected -t build.
Dependency topology
Packages form a partial order: leaf packages (utils, tokens) have no internal deps; feature packages depend on leaves; apps depend on features. Circular dependencies break the DAG and must be detected early.
Common tasks
1. Set up pnpm workspaces
pnpm-workspace.yaml:
packages:
- "apps/*"
- "packages/*"
- "tooling/*"Root package.json:
{
"name": "my-monorepo",
"private": true,
"packageManager": "pnpm@9.4.0",
"engines": { "node": ">=20.0.0", "pnpm": ">=9.0.0" },
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev --parallel",
"lint": "turbo run lint",
"test": "turbo run test"
},
"devDependencies": { "turbo": "^2.0.0" }
}Referencing an internal package:
{ "dependencies": { "@myorg/tokens": "workspace:*" } }2. Configure Turborepo
turbo.json:
{
"$schema": "https://turbo.build/schema.json",
"ui": "tui",
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["src/**", "tsconfig.json", "package.json"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"typecheck": { "dependsOn": ["^build"], "inputs": ["src/**", "tsconfig.json"] },
"lint": { "inputs": ["src/**", "eslint.config.js"] },
"test": { "dependsOn": ["^build"], "inputs": ["src/**", "tests/**"], "outputs": ["coverage/**"] },
"dev": { "cache": false, "persistent": true }
}
}Environment variable inputs (invalidate cache on env change):
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"env": ["NODE_ENV", "NEXT_PUBLIC_API_URL"],
"outputs": ["dist/**"]
}
}
}Remote caching (Vercel) + affected CI runs:
npx turbo login && npx turbo link # set up once
turbo run build --filter=...[origin/main] # CI affected builds3. Configure Nx
nx.json:
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"defaultBase": "main",
"namedInputs": {
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": ["default", "!{projectRoot}/**/*.spec.*"],
"sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
},
"targetDefaults": {
"build": { "dependsOn": ["^build"], "inputs": ["production", "^production"], "cache": true },
"test": { "inputs": ["default", "^production"], "cache": true },
"lint": { "inputs": ["default"], "cache": true }
},
"nxCloudAccessToken": "YOUR_NX_CLOUD_TOKEN"
}Affected commands:
nx show projects --affected --base=main # show affected projects
nx affected -t build # build only affected
nx affected -t test --parallel=4 # test in parallel
nx graph # visualize dependency graph4. Share TypeScript configs across packages
tooling/tsconfig/base.json:
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"skipLibCheck": true,
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}Individual package tsconfig.json:
{
"extends": "@myorg/tsconfig/base.json",
"compilerOptions": { "outDir": "dist", "rootDir": "src" },
"include": ["src"],
"exclude": ["dist", "node_modules"]
}5. Set up shared ESLint/Prettier configs
tooling/eslint-config/index.js (flat config, ESLint 9+):
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import prettierConfig from "eslint-config-prettier";
export const base = [
js.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
prettierConfig,
{
rules: {
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"@typescript-eslint/consistent-type-imports": "error",
},
},
];tooling/prettier-config/index.js:
/** @type {import("prettier").Config} */
export default { semi: true, singleQuote: false, trailingComma: "all", printWidth: 100 };6. Implement internal packages (tsup)
packages/utils/package.json:
{
"name": "@myorg/utils",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" }
},
"scripts": { "build": "tsup", "dev": "tsup --watch" },
"devDependencies": { "tsup": "^8.0.0" }
}tsup.config.ts:
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm", "cjs"],
dts: true,
sourcemap: true,
clean: true,
});For packages consumed only within the repo (no publish), skip the build step
entirely and use TypeScript path aliases in tsconfig.base.json:
{ "compilerOptions": { "paths": { "@myorg/utils": ["packages/utils/src/index.ts"] } } }7. Choose Turborepo vs Nx vs Bazel
See references/tool-comparison.md for the full feature matrix.
| Team / project profile | Recommended tool |
|---|---|
| JS/TS monorepo, small-medium team, fast setup | Turborepo |
| JS/TS monorepo, want generators + boundary enforcement | Nx |
| Polyglot repo (Go, Java, Python + JS), 100+ packages | Bazel |
| Already on Nx Cloud, need distributed task execution | Nx |
| Migrating from Lerna | Turborepo (drop-in) or Nx (migration tooling) |
Quick rule: Start with Turborepo. Upgrade to Nx when you need project
generators, @nx/enforce-module-boundaries, or Nx Cloud DTE. Only adopt Bazel
for a genuinely polyglot repo with build engineering capacity.
Anti-patterns / common mistakes
| Anti-pattern | Problem | Fix |
|---|---|---|
| Relying on hoisted node_modules for unlisted deps | Breaks when hoisting changes; silent cross-package contamination | Declare every dep in the package that uses it |
"outputs": ["**"] in turbo.json |
Caches node_modules, inflates cache size, poisons hits | List only build artifacts: dist/**, .next/** |
Missing "dependsOn": ["^build"] on build task |
Downstream packages build before deps are ready; missing types/files | Always set ^build dependsOn for build tasks |
| Circular workspace dependencies | Breaks the task DAG; tools silently skip or hang | Use nx graph or madge to detect; enforce via lint |
| Publishing internal packages to npm to share within the repo | Introduces a publish cycle where workspace:* suffices |
Use workspace protocol; only publish genuinely public packages |
Gotchas
Turborepo cache poisoning from over-broad
outputs- Setting"outputs": ["**"]in a task cachesnode_modules/,.git/, and generated files alongside build artifacts. A cache hit then restores stalenode_modulesfrom a previous run, causing dependency resolution bugs that are nearly impossible to trace. List only specific artifact directories:dist/**,.next/**,coverage/**.workspace:*vsworkspace:^produce different lockfile behavior -workspace:*keeps the version as*in the lockfile and always resolves to whatever version the local package is at.workspace:^pins a semver range at install time. Mixing both across packages in the same repo produces inconsistent resolution and breaks remote cache hits. Choose one convention and enforce it.Missing
"dependsOn": ["^build"]causes type errors in downstream packages - Without this directive, Turborepo may start building a consumer package before its dependency has emitted itsdist/output and type declarations. TypeScript errors likeCannot find module '@myorg/utils'are the symptom. Always declare^builddependsOn for any task that produces files consumed by other packages.Nx affected commands use
defaultBasewhich defaults tomainbut CI often checks out a detached HEAD -nx affectedcomputes affected projects by diffing againstdefaultBase. In a GitHub Actions PR workflow withactions/checkout@v4, the base branch is not fetched by default. Addfetch-depth: 0to the checkout step and set--base=origin/mainexplicitly, ornx affectedwill compare against nothing and rebuild everything.Circular workspace dependencies hang builds silently - A circular dependency between packages (A depends on B, B depends on A) breaks the task DAG and causes Turborepo or Nx to either deadlock or skip tasks without error messages. Run
nx graphormadge --circular --extensions ts packages/regularly to detect cycles before they cause build failures in CI.
References
- Turborepo docs
- Nx docs
- Bazel docs
- pnpm workspaces
- tsup
- changesets - versioning/publishing (Lerna replacement)
references/tool-comparison.md- detailed Turborepo vs Nx vs Bazel vs Lerna comparison
References
tool-comparison.md
Monorepo Tool Comparison: Turborepo vs Nx vs Bazel vs Lerna
Overview
| Dimension | Turborepo | Nx | Bazel | Lerna |
|---|---|---|---|---|
| Maintained by | Vercel | Nrwl / Nx Inc. | Google / community | Nrwl (maintenance mode) |
| Language focus | JS/TS only | JS/TS primary, plugins for others | Polyglot | JS/TS only |
| Primary function | Task runner + cache | Task runner + project graph + generators | Hermetic build system | Package versioning + publish |
| Config complexity | Low | Medium | High | Low |
| Learning curve | Low | Medium | High | Low |
| Remote caching | Vercel (free tier), self-host | Nx Cloud (free tier), self-host | Remote execution API (RBE) | None built-in |
| License | MIT | MIT | Apache 2.0 | MIT |
Turborepo
What it is
Turborepo (owned by Vercel, written in Rust) is a high-performance task runner for JavaScript and TypeScript monorepos. It focuses on one thing: running tasks in the correct order with maximum parallelism and a content-addressed local and remote cache.
Strengths
- Zero-config adoption - Drop
turbo.jsoninto any existing pnpm/npm/yarn workspace and it works. No need to restructure packages or add config per project. - Fastest cold starts - The Rust-based engine starts and schedules tasks faster than any JS-based alternative.
- Pipeline simplicity - The
taskssection ofturbo.jsonis easy to reason about.dependsOn: ["^build"]is the entire API for ordering. - Vercel remote cache - Free tier available. Enterprise teams can self-host using the open HTTP Remote Cache API compatible with S3, GCS, or custom servers.
--filtersyntax - Powerful and composable:--filter=@scope/*,--filter=...[HEAD^1],--filter=./apps/*.- Turborepo TUI - Interactive terminal UI shows running tasks, logs, and
cache status in real time (
"ui": "tui"in turbo.json).
Weaknesses
- No generators - No scaffolding or code generation. You need separate tools (plop, hygen, custom scripts).
- No module boundary enforcement - Cannot statically prevent
app-afrom importingapp-b's internals. Nx's@nx/enforce-module-boundariesdoes this. - JS/TS only - Cannot orchestrate builds for Go, Rust, Java, or Python packages in the same repo.
- Less plugin ecosystem - No official integrations for storybook, Next.js migrations, module federation, etc. compared to Nx plugins.
- Affected analysis is simpler -
--filter=...[base]works on package granularity, not file granularity (Nx is more precise vianamedInputs).
Best for
Small to medium JS/TS teams that want fast builds with minimal setup. Ideal as a first monorepo tool. Teams already on Vercel benefit most from the remote cache integration.
Setup time
15-30 minutes to add to an existing workspace.
Nx
What it is
Nx (by Nrwl, written in TypeScript with a Rust-based task runner since v17) is a full-featured build system and developer platform. It combines task orchestration with a project graph, workspace generators, module boundary enforcement, and a rich plugin ecosystem.
Strengths
- Project graph visualization -
nx graphrenders an interactive dependency graph of all projects and their relationships. - Generators and migrations -
nx generatescaffolds new apps, libs, and components following workspace conventions.nx migrateautomates dependency upgrades across the repo. - Module boundary enforcement - The
@nx/enforce-module-boundariesESLint rule uses projecttagsto enforce architectural constraints (e.g., feature libs cannot import from app libs). - Nx Cloud - First-party remote caching and distributed task execution (DTE). DTE splits a task graph across multiple CI agents for maximum parallelism.
- Rich plugin ecosystem - Official plugins for Next.js, React, Angular, NestJS, Vite, Storybook, Cypress, Playwright, Docker, and more.
namedInputsprecision - Fine-grained cache input definitions allow per-project cache granularity beyond package-level boundaries.- Affected commands -
nx affected -t buildis precise to the project graph, not just changed files.
Weaknesses
- More ceremony - Each project needs a
project.json. Configuration is verbose compared to Turborepo. - Plugin coupling - Nx plugins often abstract underlying tools (e.g., Vite, Jest) behind Nx executors. This adds a layer that can lag behind upstream tool releases.
- Nx Cloud costs - The free tier has limits. Larger teams need paid plans. Self-hosting is possible but more complex than Turborepo's cache API.
- Steeper learning curve - Concepts like executors, generators,
project.json, and tags require investment to understand fully.
Best for
Medium to large JS/TS teams that want generators, enforced architecture, and distributed task execution. Especially strong for Angular or large React enterprise workspaces, or teams that want Nx Cloud's distributed CI.
Setup time
1-4 hours depending on existing workspace structure. Full Nx workspace from
scratch: npx create-nx-workspace.
Bazel
What it is
Bazel (open-sourced from Google's internal Blaze) is a hermetic, reproducible build system designed for polyglot monorepos at massive scale. It guarantees that given the same inputs, the same outputs are produced on any machine.
Strengths
- True hermeticity - Every build rule declares all its inputs and outputs explicitly. Builds cannot accidentally depend on the host environment.
- Polyglot - First-class support for Java, Go, C++, Python, Rust, and JavaScript (via rules_nodejs or aspect-build/rules_js). One tool for the entire repo regardless of language.
- Remote execution (RBE) - Bazel can distribute individual build actions to a cluster of remote workers (Google RBE, BuildBuddy, EngFlow). This is beyond caching - it parallelizes at the action level, not the task level.
- Precise incremental builds - Bazel tracks file-level inputs, not package-level. Only the exact set of affected actions re-runs.
- Scales to enormous repos - Used by Google (hundreds of millions of LOC), Stripe, Canva, LinkedIn, Twitter, and others.
Weaknesses
- Very high learning curve - Starlark (Bazel's Python-like build language),
BUILD.bazelfiles, toolchain configuration, and remote execution setup all require significant investment. - JS ecosystem friction - The JavaScript ecosystem was not designed with
Bazel in mind.
rules_js(by aspect-build) is the best current solution but still requires migration effort. - Maintenance overhead - You need dedicated build engineering capacity to maintain Bazel in a large repo. Small teams should not adopt it.
- Slow cold builds - The first build is often slower than other tools due to toolchain downloading and action graph construction.
- No remote cache free tier - Remote caching requires infrastructure (GCS, S3, BuildBuddy, EngFlow). BuildBuddy offers a free tier for open source.
Best for
Large organizations with 50+ packages, polyglot repos, or Google-scale build requirements. Only worthwhile if you have engineers willing to invest in build infrastructure.
Setup time
Days to weeks for a meaningful integration. Not suitable for teams without build engineering resources.
Lerna (deprecated / maintenance mode)
What it is
Lerna was the original JavaScript monorepo tool, popularized around 2017-2018. It managed package versioning, changelog generation, and publishing for multi-package repos. Nrwl took over maintenance in 2022.
Current status
Lerna is in maintenance mode. Nrwl recommends migrating to Nx + Nx release (for versioning/publishing) or to Turborepo. New projects should not use Lerna.
What it did well (historically)
- Conventional commits-based versioning (
lerna version) - Coordinated publishing to npm (
lerna publish) lerna runfor running scripts across packages
Why it fell behind
- No meaningful caching (unless integrating Nx under the hood)
- Slow task execution - no parallelism or DAG-aware scheduling
- Complex
independentvsfixedversioning modes caused confusion - pnpm/yarn workspaces made the dependency management parts redundant
Migration path
- Task orchestration: migrate to Turborepo or Nx
- Publishing/versioning: use
changesets(recommended),nx release, orsemantic-release
Feature comparison matrix
| Feature | Turborepo | Nx | Bazel | Lerna |
|---|---|---|---|---|
| Local task caching | Yes | Yes | Yes | No |
| Remote caching | Yes (Vercel/self-host) | Yes (Nx Cloud/self-host) | Yes (RBE/GCS/S3) | No |
| Affected analysis | Package-level | Project-level (precise) | Action-level (most precise) | No |
| Parallel task execution | Yes | Yes | Yes (distributed) | Limited |
| Code generators | No | Yes | No (macros only) | No |
| Module boundary enforcement | No | Yes (ESLint rule) | Yes (visibility) | No |
| Project graph visualization | No | Yes (nx graph) |
Via Bazel query | No |
| Distributed task execution | No | Yes (Nx Cloud DTE) | Yes (RBE) | No |
| Polyglot support | No | Partial (via plugins) | Yes (first-class) | No |
| Package versioning/publish | No | Yes (nx release) |
No | Yes (legacy) |
| Learning curve | Low | Medium | High | Low |
| Config verbosity | Low | Medium-High | High | Low |
| Community/ecosystem | Growing | Large | Large (non-JS) | Declining |
When to migrate between tools
Turborepo to Nx: When you need generators to enforce workspace conventions, module boundary rules to prevent architectural violations, or Nx Cloud's distributed task execution for long CI pipelines.
Nx to Bazel: When you have a genuine polyglot repo (non-trivial Java/Go/Rust alongside JS), when you hit scaling limits of Nx's JS-centric model, or when you need true hermetic builds for compliance/security reasons.
Lerna to Turborepo: One-day migration. Remove lerna.json, add turbo.json.
Move versioning to changesets. Done.
Lerna to Nx: Use the official nx migrate tooling which automates most of
the conversion.
Remote caching options by tool
| Provider | Turborepo | Nx | Bazel |
|---|---|---|---|
| Vercel Remote Cache | Native | No | No |
| Nx Cloud | No | Native | No |
| BuildBuddy | No | No | Yes |
| EngFlow | No | No | Yes |
| Self-hosted S3/GCS | Yes (cache API) | Yes (custom runner) | Yes (RBE) |
| GitHub Actions Cache | Yes (community) | Yes (community) | Limited |
References
- Turborepo docs
- Turborepo remote caching
- Nx docs
- Nx Cloud
- Nx affected
- Bazel docs
- aspect-build/rules_js - best JS/TS rules for Bazel
- BuildBuddy - Bazel remote cache/execution
- changesets - versioning/publishing alternative to Lerna
- Lerna deprecation notice
Frequently Asked Questions
What is monorepo-management?
Use this skill when setting up or managing monorepos, configuring workspace dependencies, optimizing build caching, or choosing between monorepo tools. Triggers on Turborepo, Nx, Bazel, pnpm workspaces, npm workspaces, yarn workspaces, build pipelines, task orchestration, affected commands, and any task requiring multi-package repository management.
How do I install monorepo-management?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill monorepo-management in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support monorepo-management?
monorepo-management works with claude-code, gemini-cli, openai-codex. Install it once and use it across any supported AI coding agent.