Claude Code monorepo setup for large engineering teams
<article> <p>A monorepo team usually notices the problem in a small, annoying way. Claude Code starts confidently, reads a few files, then reaches for the wrong package convention. Or it spends context on generated clients nobody meant it to inspect. Or one developer starts from the repo root while another starts inside <code>packages/api</code>, and both assume they are using the same project setup.</p>
<p>The tool is not broken in that situation. The setup is ambiguous.</p>
<p>A good <strong>Claude Code monorepo setup</strong> is less about writing one perfect root prompt and more about deciding who owns context at each layer of the repository. The root should explain the map. Packages should explain their local rules. Settings should enforce what guidance cannot. Skills should hold repeatable procedures that do not need to load every session.</p>
<p>That distinction matters because monorepos punish vague configuration. A single repository can contain frontend apps, backend services, shared types, migrations, generated SDKs, build tooling, infrastructure code, and legacy pockets with their own rules. If Claude sees all of that as one flat project, it will carry too much. If Claude sees too little, it will miss the dependency boundary that makes the change safe.</p>
<h2>The first decision is where Claude starts</h2>
<p>Launch directory is not a convenience detail. It decides the first shape of the session.</p>
<p>When you start Claude Code from the repository root, it has default access to the whole tree and loads the root <code>CLAUDE.md</code> at launch. Subdirectory <code>CLAUDE.md</code> files are not all pulled in immediately. They come into play when Claude reads files under those directories.</p>
<p>When you start from a package or subsystem, Claude gets a narrower default file surface. It loads that directory's <code>CLAUDE.md</code> and the ancestor files above it. In practice, starting from <code>packages/api</code> should give Claude the root map plus API-specific instructions, without carrying the web app's local workflow from the beginning.</p>
<p>That gives teams a simple operating rule:</p>
<ul> <li>Start at the package when the task is local.</li> <li>Start at the root when the task is genuinely cross-package.</li> <li>Add sibling access deliberately when a local task needs shared code.</li> </ul>
<p>This is also where several field complaints about monorepos come from. Developers expect every <code>.claude</code> surface to behave like <code>CLAUDE.md</code>, walking upward and merging naturally. It does not all work that way. Project settings are tied to the starting directory, so a root <code>.claude/settings.json</code> applies when you start at the root, but a package-level launch may need a self-contained package settings file.</p>
<p>That mismatch is easy to miss during a demo and painful in daily use.</p>
<h2>Use the root file as a map, not a manual</h2>
<p>The root <code>CLAUDE.md</code> should answer one question quickly: what kind of repository is this, and where should Claude go next?</p>
<p>It should not contain every test command, migration warning, UI style rule, database caveat, and package exception. Anthropic's guidance is to keep <code>CLAUDE.md</code> files short, with a target under 200 lines. The reason is practical: always-loaded instructions consume context, and long instruction files tend to reduce adherence rather than improve it.</p>
<p>A useful root file usually contains:</p>
<ul> <li>Top-level repository layout.</li> <li>Ownership boundaries between major packages.</li> <li>Globally true build and test commands.</li> <li>Repo-wide gotchas that apply everywhere.</li> <li>Where to find local package instructions.</li> </ul>
<p>The package file should carry the local facts:</p>
<ul> <li>The package's test command.</li> <li>Local framework conventions.</li> <li>Migration or codegen steps.</li> <li>Directory patterns that differ from the rest of the repo.</li> <li>Known pitfalls owned by that team.</li> </ul>
<p>For example:</p>
<pre><code>repo/ CLAUDE.md .claude/ settings.json rules/ migrations.md react-components.md packages/ api/ CLAUDE.md .claude/ settings.json skills/ add-endpoint/ SKILL.md web/ CLAUDE.md .claude/ skills/ add-route/ SKILL.md</code></pre>
<p>The root file gives orientation. The API file explains API work. The web file explains web work. Rules and skills handle cross-cutting guidance without turning the root into a dumping ground.</p>
<p>If your team is already standardizing agent instructions across tools, this is a good place to create an internal link anchor for <a href="/agents-md-vs-claude-md-vs-cursor-rules" data-internal-link="true">AGENTS.md vs CLAUDE.md vs Cursor rules</a>, because duplicated instructions are one of the fastest ways to make monorepo behavior drift.</p>
<h2>Separate guidance from enforcement</h2>
<p>One of the most common mistakes is putting hard rules into <code>CLAUDE.md</code> and treating the file as a policy engine.</p>
<p>It is not. <code>CLAUDE.md</code> is context. It can guide behavior, but it is not the right place for controls that must always be blocked or guaranteed.</p>
<p>Use settings, permissions, managed policy, sandboxing, and hooks for enforcement. Use <code>CLAUDE.md</code> for durable human-readable context. Use hooks when an action needs a check at a specific lifecycle point, such as before a tool call or after instructions load. Use managed settings when a rule must sit above project and local overrides.</p>
<p>This matters for security, but also for developer experience. "Never read generated files" should not rely only on prose. Committed generated clients, vendor folders, build artifacts, and lockstep output can burn context fast. Keep noise out with <code>.gitignore</code> where appropriate and add explicit <code>Read</code> deny rules for committed artifacts that should not be inspected.</p>
<h2>Path-scoped rules beat giant imports</h2>
<p><code>@path</code> imports are useful for organizing instructions for humans. They are not a context-saving mechanism. Imported files expand into context at launch, so splitting a large root file into imported fragments can make the repo look tidier without reducing the actual load.</p>
<p>For monorepos, path-scoped rules are usually the better tool. Files in <code>.claude/rules/*.md</code> can be modular, and rules with YAML <code>paths</code> frontmatter load only when Claude works with matching files. That lets a migration rule follow migration files, or a React component rule follow frontend component paths, without forcing every session to carry it.</p>
<p>Use skills for procedures. A package-level skill under <code>packages/api/.claude/skills/</code> can describe how to add an endpoint, run the right checks, and update local docs. The skill body loads on demand when relevant, rather than every time someone opens Claude in the repo.</p>
<p>The trade-off is discoverability. Skill names and descriptions still form a selection surface. If every package creates a large catalog with vague descriptions, selection gets noisy. Keep descriptions short, specific, and keyword-led.</p>
<h2>Cross-package work needs explicit access</h2>
<p>Monorepo changes often start local and then touch a sibling. An API change needs shared types. A web change needs generated client behavior. A package migration needs root build config.</p>
<p>Claude Code gives you more than one way to handle that, and the difference matters.</p>
<p><code>additionalDirectories</code> grants access to other directories, including siblings or other repos. But it does not load their <code>CLAUDE.md</code>, rules, or skills. It is access, not instruction discovery.</p>
<p><code>--add-dir</code> or <code>/add-dir</code> can add directories during a session and can load skills. Loading <code>CLAUDE.md</code> and rules from added directories requires the relevant environment variable described in the official docs.</p>
<p>That distinction prevents a subtle production failure. A developer may add access to <code>packages/web</code> while working from <code>packages/api</code> and assume web conventions loaded too. Unless configured for that, Claude can read the files but not necessarily carry the web package's local instructions.</p>
<p>For larger cross-package changes, root launch can be the cleaner choice. For package-local work with a known dependency, start local and add only the sibling directories needed. Teams using <a href="/claude-code-plan-mode-workflow-for-teams" data-internal-link="true">Claude Code Plan Mode workflows</a> should make this part of the plan review: where is the session rooted, and what additional directories are intentionally in scope?</p>
<h2>Compaction changes what Claude remembers</h2>
<p>Long monorepo tasks hit compaction. When that happens, not every instruction source comes back in the same way.</p>
<p>Project-root <code>CLAUDE.md</code>, unscoped rules, and auto memory are re-injected after compaction. Nested <code>CLAUDE.md</code> files and path-scoped rules are not automatically re-injected until Claude reads matching files again. Invoked skill bodies have their own documented behavior and token caps.</p>
<p>The practical consequence is simple: after compaction, a package-local assumption may disappear until the session touches that package again. For long refactors, ask Claude to keep plans, decisions, and package-specific notes in a task file inside the workspace, then verify with <code>/context</code> when behavior looks off.</p>
<p>This is also where <a href="/claude-code-checkpoints-vs-git-commits" data-internal-link="true">Claude Code checkpoints vs git commits</a> becomes relevant. Checkpoints can help with session-level recovery, but they do not replace intentional commits or a written migration plan when several packages are changing.</p>
<h2>Use code intelligence, not brute-force reading</h2>
<p>Large-codebase work should not mean reading large files until the context window fills. Claude Code works against live files, which is useful because it avoids stale indexes. But live navigation still needs boundaries.</p>
<p>For large monorepos, use language server or code-intelligence plugins where available. Symbol navigation is usually better than broad scans when the question is "where is this function defined?" or "what calls this type?" Keep generated, vendor, and build output away from routine reads. Use subagents for research-heavy exploration when the parent session only needs summarized findings.</p>
<p><code>/context</code> is the runtime diagnostic to check what loaded and how much context is being used. <code>/doctor</code> can help diagnose setup and suggest trims or migration from always-loaded guidance into nested files and skills.</p>
<p>For UI testing or browser automation work, it is worth linking from this section to <a href="/playwright-cli-vs-mcp-for-claude-code" data-internal-link="true">Playwright CLI vs MCP for Claude Code</a>, because tool choice affects how much output gets pushed back into the session.</p>
<h2>Make governance boring on purpose</h2>
<p>The best monorepo setups are not clever. They are owned.</p>
<p>Someone has to decide what belongs in the root, what package owners maintain, which settings are committed, which policies are managed, and how hooks are reviewed. Anthropic's large-codebase guidance points to a platform or developer-experience owner, sometimes described as an agent manager, working with security and governance early.</p>
<p>That ownership should cover:</p>
<ul> <li>Root <code>CLAUDE.md</code> size and content review.</li> <li>Package <code>CLAUDE.md</code> ownership, usually aligned with code ownership.</li> <li>Rules and skills naming conventions.</li> <li>Permission defaults in committed project settings.</li> <li>Managed settings for non-negotiable controls.</li> <li>Hook review, especially because hooks run commands with the user's permissions.</li> <li>Periodic cleanup after model or repository changes.</li> </ul>
<p>That last point is easy to skip. Instructions written to compensate for an older model can become drag later. The briefed Anthropic guidance recommends reviewing configuration every three to six months or after major releases. Treat that as maintenance, not polish.</p>
<h2>A practical Claude Code monorepo setup checklist</h2>
<p>If you are bringing Claude Code into an existing monorepo, start with the smallest structure that makes ownership clear.</p>
<ol> <li>Write a lean root <code>CLAUDE.md</code> that maps the repo and names global rules.</li> <li>Add package <code>CLAUDE.md</code> files only where local conventions are real and useful.</li> <li>Move cross-cutting file-specific guidance into path-scoped rules.</li> <li>Move repeatable procedures into package skills.</li> <li>Commit project settings for shared defaults.</li> <li>Use local settings only for personal workflow preferences.</li> <li>Put hard controls in managed settings, permissions, sandboxing, or hooks.</li> <li>Deny reads for committed generated, vendor, and build artifacts that waste context.</li> <li>Define when developers should start at root versus inside a package.</li> <li>Document how to add sibling access and what that does not load.</li> <li>Use <code>/context</code> during rollout to verify what is actually loaded.</li> <li>Review the setup on a fixed cadence as the repo and Claude Code change.</li> </ol>
<p>The main shift is cultural, not syntactic. Claude Code performs better in a monorepo when the repository tells it where it is, what matters locally, and which boundaries are real. Once those responsibilities are explicit, the setup stops feeling like a pile of prompts and starts looking like developer infrastructure.</p> </article>