Using Claude Code with Monorepos: Configuration and Scoping Strategies
Monorepos present a specific challenge for Claude Code that single-package repositories do not.
In a standard project, Claude Code reads the codebase and builds a complete model quickly. In a monorepo with 20 packages and hundreds of thousands of lines of code, "read the codebase" is not a practical instruction.
The right way to use Claude Code in a monorepo is fundamentally different from a single-package project. Session scope, CLAUDE.md architecture, and task structure all require deliberate adjustment.
A monorepo is not a big single-package repo. It is a coordination mechanism for related but distinct packages. Claude Code sessions should reflect that distinction: scoped to the package, not to the repository.
Why Monorepos Require a Different Approach
Three properties of monorepos create challenges that do not exist in standard repositories:
Large file count. A monorepo with 15 packages might contain 50,000 files. Claude Code reading the entire repository in a single session is inefficient and often unnecessary. Most tasks are scoped to one or two packages. Flooding the context with all 15 packages increases noise without improving output quality.
Package interdependencies. Changes to a shared package affect all packages that depend on it. Claude Code working in packages/api may not know that packages/web and packages/mobile both import from packages/shared-types.
A change to the shared types that looks clean in isolation breaks downstream consumers.
Workspace tooling. Monorepos typically use workspace-aware package managers and build orchestrators: pnpm workspaces, Turborepo, Nx, or Lerna. The build commands, test commands, and task pipelines are different from a standard npm project.
Claude Code needs to know how to run tests correctly: pnpm turbo test --filter=@myorg/api is not the same as npm test.
CLAUDE.md Strategy: Two Levels
The single most important configuration decision for Claude Code in a monorepo is using a two-level CLAUDE.md architecture.
Root CLAUDE.md
The root CLAUDE.md covers repository-wide context that applies regardless of which package Claude Code is working in:
# Monorepo Overview
## Structure
This is a pnpm workspace monorepo using Turborepo.
- packages/api — Express REST API
- packages/web — Next.js frontend
- packages/mobile — React Native app
- packages/shared-types — Shared TypeScript interfaces (used by all packages)
- packages/ui-components — Shared React components (used by web and mobile)
## Workspace Commands
- Build all: `pnpm turbo build`
- Test all: `pnpm turbo test`
- Test a specific package: `pnpm turbo test --filter=@myorg/<package-name>`
- Add a dependency to a package: `pnpm add <package> --filter=@myorg/<package-name>`
## Critical: Shared Package Rules
Modifications to packages/shared-types or packages/ui-components affect all consumers.
Always run `pnpm turbo build --filter=@myorg/shared-types...` after changes to shared packages
to confirm no downstream build failures.
## Do Not Modify
- turbo.json — pipeline configuration managed by ops
- pnpm-workspace.yaml — workspace definition; changes require ops review
Package-Level CLAUDE.md
Each package that Claude Code works in frequently should have its own CLAUDE.md in the package directory:
# packages/api
## Purpose
REST API for the platform. Handles authentication, orders, and customer data.
## Package Commands
- Run: `pnpm dev` (from this directory)
- Test: `pnpm test`
- Test with coverage: `pnpm test:coverage`
- Lint: `pnpm lint`
## Conventions
- Route handlers go in src/routes/
- Business logic goes in src/services/ (never in route handlers)
- Database queries go in src/repositories/
- All endpoints require authentication middleware except /health and /auth/*
## Dependencies on Shared Packages
- Imports types from @myorg/shared-types
- Do not add UI component imports; this is an API package
The two-level structure means Claude Code in a package session reads both the root context (monorepo structure, workspace tooling, shared package rules) and the package context (specific conventions, commands, dependencies). Neither document is overloaded with information that belongs in the other.
Scoping Sessions: Package Directory, Not Repo Root
The default for most Claude Code sessions in a monorepo should be starting from the relevant package directory:
# Scoped to the API package
cd packages/api
claude "add a GET /customers/:id endpoint with full test coverage"
Starting from the package directory serves two purposes. First, Claude Code’s file reads are bounded to the package by default. It will not accidentally modify files in adjacent packages without a specific reason. Second, the test and build commands in the package-level CLAUDE.md are relative to the package directory and will work correctly.
When to start from the repo root instead:
- Cross-package changes that must be made together (updating shared types and their consumers simultaneously)
- Repository-level configuration changes (Turborepo pipeline updates, CI configuration)
- Audits that need to span multiple packages (“find all places we import from @myorg/shared-types”)
For cross-package sessions from the repo root, be more explicit about scope boundaries in the prompt to prevent Claude Code from ranging beyond the intended packages.
Handling Cross-Package Changes
Cross-package changes are the highest-risk category in monorepo development. A change to packages/shared-types that modifies an interface must be accompanied by corresponding updates in every package that implements or consumes that interface.
Commit them atomically. Cross-package changes that are split across multiple commits create windows where the build is broken.
Structure the prompt to make atomic commitment explicit:
Update the Order type in packages/shared-types to add an optional `notes` field.
Then update all packages that use the Order type to handle the new optional field:
- packages/api: update order creation endpoint to accept notes
- packages/web: update the order form to display the notes field
Verify the full build passes with `pnpm turbo build` before committing.
Commit all three packages together in a single commit.
Including the build verification command is critical. Turborepo will catch downstream failures. Claude Code needs to run it and read the output.
Turborepo and Nx Integration
Claude Code needs to know how your build orchestrator works. Include the key commands in the root CLAUDE.md.
For Turborepo:
## Build System: Turborepo
Test a single package and its dependencies:
`pnpm turbo test --filter=@myorg/api`
Test a package and all packages that depend on it:
`pnpm turbo test --filter=...@myorg/shared-types`
Build only the changed packages (Turbo cache-aware):
`pnpm turbo build`
Run tasks in a specific package:
`pnpm turbo dev --filter=@myorg/web`
For Nx:
## Build System: Nx
Test a single project:
`nx test api`
Test all affected projects from a change:
`nx affected --target=test`
Build a project:
`nx build web`
Without these commands, Claude Code will default to running npm test from the wrong directory, producing test runs that either fail immediately or test the wrong package.
Using —add-dir for Shared Package Access
When working in a scoped package session, Claude Code may need to read files from shared packages to understand the types and interfaces it is working with.
The --add-dir flag gives Claude Code read access to additional directories without changing the working directory:
cd packages/api
claude --add-dir ../shared-types "implement the new CustomerProfile interface from shared-types"
Claude Code can now read packages/shared-types while its working directory and write operations remain scoped to packages/api. This prevents cross-package writes while enabling cross-package reads.
Use --add-dir when:
- The task requires understanding types defined in shared packages
- The task requires reading examples from other packages for pattern reference
- Cross-package reads are needed but cross-package writes are not intended
Git Worktrees for Parallel Package Work
Monorepos benefit significantly from worktrees when work on different packages is genuinely independent:
# Work on API and web frontend simultaneously
git worktree add -b feature/api-customer-profile ../myapp-api main
git worktree add -b feature/web-customer-profile ../myapp-web main
Each worktree is the full monorepo, but checked out to its own branch. A Claude Code session in ../myapp-api works on the API implementation. A concurrent session in ../myapp-web works on the frontend. Both are isolated.
When the work requires a shared-types change, use a sequential handoff: complete the shared-types update in one worktree, push it to a branch, and configure the other worktree to pull from that branch before proceeding with the consumer changes. This prevents both worktrees from independently modifying shared types in conflicting ways.
Frequently Asked Questions
Should every package in the monorepo have its own CLAUDE.md?
Not necessarily. Packages that Claude Code never works in do not need CLAUDE.md. Start by adding CLAUDE.md to the packages you actively develop with Claude Code. The three packages touched most frequently, typically the API, the primary frontend, and any shared library, are the highest-priority. Add package-level CLAUDE.md to others as Claude Code starts working in them.
How do I prevent Claude Code from accidentally modifying shared packages when it is scoped to one package?
The combination of starting Claude Code from the package directory and using --allowedTools to restrict write access provides the strongest boundary. For example: claude --allowedTools "Edit:packages/api/**,Read:packages/**" allows reads across the monorepo but restricts writes to the api package.
This prevents accidental shared-type modifications without preventing the cross-package reads that are often needed for context.
Claude Code keeps using the wrong test command for the monorepo. How do I fix this?
This is a CLAUDE.md completeness issue. Add the exact test commands to the root CLAUDE.md and the relevant package CLAUDE.md. Be explicit: write the full command including the package filter, not just pnpm test.
If Claude Code is still defaulting to the wrong command, add a line to CLAUDE.md that says “Never run plain npm test or pnpm test from the repo root. Always use the Turborepo filter command.”
Is it better to have one large Claude Code session for a cross-package change or separate sessions per package?
It depends on the interdependency. If the cross-package change is a simple type addition where all packages need to be updated simultaneously, one session from the repo root with an atomic commit instruction is cleaner. If the packages have distinct, complex implementations that each require substantial work, separate sessions with clearly defined interface contracts between them produce better results. Define the shared interface first, then delegate each package implementation to its own scoped session.
Ready to configure Claude Code for your monorepo?
Start by creating a two-level CLAUDE.md structure, one at the repo root, one per package, and scope your first session to a single package to see how much the boundary improves output quality.
Path one: set it up yourself. Follow the CLAUDE.md strategy and scoping patterns in this article, then layer in worktrees once you have parallel package work to handle. The Claude Code course covers configuration patterns like these in a structured workflow.
Path two: work with Phos AI Labs. If you want Claude Code configured for a complex monorepo environment, including the CLAUDE.md architecture, scoping strategies, and worktree patterns that make AI-assisted development reliable across packages, we handle that setup. Phos AI Labs is a CCA-F certified Claude implementation partner. Thirty minutes, no deck. Start here.