Skip to content

Claude Code works in a large codebase or monorepo when each session sees only the slice a task touches. The launch directory, per-directory CLAUDE.md files, claudeMdExcludes, Read deny rules, a code intelligence plugin, and sparse worktrees control that slice. Cross-package changes stay safe when they are planned in one session and checked by gates on every affected package.

This page is for developers working in a repository too big to hold in their head, and for tech leads who own its shared Claude Code configuration. The situation: you ask Claude to change the auth flow in a 40-package monorepo, and it edits the deprecated legacy-auth package instead of the live one. Two weeks later a teammate’s session reads the frontend team’s CLAUDE.md and applies React rules to a Go service. Neither is a model problem. Both are scoping problems.

Everything below was checked against Claude Code v2.1.283 (the latest channel) and the vendor’s large codebases guide on 2026-09-26.

What you get from scoping Claude Code to a large codebase

Section titled “What you get from scoping Claude Code to a large codebase”
  • A rule for where to start claude so the right CLAUDE.md files and settings load.
  • A committed settings file that keeps other teams’ instructions, build output, and vendored code out of context.
  • Prompts for a complete caller inventory, a cross-package plan, and a gated execution run.
  • A worktree setup that lets parallel sessions work without overwriting each other.
  • A verification script that proves scope, completeness, and behaviour on every affected package, so nobody has to read every line.
  • Recovery steps for the failures that only show up at scale.

Where should you start Claude in a large repository?

Section titled “Where should you start Claude in a large repository?”

The directory you launch claude from decides what Claude can edit without asking, which CLAUDE.md files load at startup, and which .claude/settings.json applies. Project settings are not inherited from parent directories the way CLAUDE.md files are.

Start fromFile accessCLAUDE.md loaded at launchUse when
Repository rootEvery fileRoot only; a subdirectory’s file loads when Claude reads a file thereThe task spans several packages or subsystems
A package or subsystemThat subtree, until you grant moreThat directory’s file and every ancestor’sThe work belongs to one package

Default to the package. Start from the root only when the change genuinely crosses packages, and then plan it first (see plan a cross-package change).

To confirm what loaded, run /context in the session and read the Memory files list. If a file from another team’s package appears there, the next section removes it.

Each setting below is independent, so adopt the ones your repository needs. The tech lead commits the shared parts; each developer keeps personal exclusions in .claude/settings.local.json.

  1. Split CLAUDE.md by directory. Keep the root file to rules that apply everywhere (commit format, where generated code lives, how to run package scripts). Put each package’s stack conventions in that package’s CLAUDE.md, maintained by its owners. For the full loading order, imports and .claude/rules/ with paths: globs, see the Claude Code memory system.

    packages/api/CLAUDE.md
    Copy `.env.example` to `.env` before running tests; they fail without it.
    Write queries with the Knex query builder. No raw SQL in route handlers.
    Never edit a merged migration. Add a new one.
    Gate: `pnpm --filter api run typecheck && pnpm --filter api test`.
  2. Exclude instructions for code you never touch. When you start from the root, a package’s CLAUDE.md loads as soon as Claude reads a file in it. claudeMdExcludes skips files by glob, matched against absolute paths, so start relative patterns with **/. Put personal exclusions in .claude/settings.local.json and add that file to .gitignore if you created it by hand. Managed-policy CLAUDE.md files cannot be excluded.

    {
    "claudeMdExcludes": ["**/packages/legacy-*/**", "**/packages/web/CLAUDE.md"]
    }
  3. Deny reads of generated and vendored code. Content search already respects .gitignore, so node_modules/ and dist/ stay out of results. For checked-in generated code or a vendored SDK, add Read deny rules. Ending a pattern with /**/* blocks the files but still lets Claude list the directory. Deny rules cover Claude’s file tools and the Bash file commands Claude Code recognises (cat, grep, find with the path as an argument), but a recursive grep -r over a parent directory still returns matches from denied files, and subprocesses that open files themselves are not covered.

    {
    "permissions": {
    "deny": [
    "Read(./**/generated/**/*)",
    "Read(./**/*.generated.*)",
    "Read(./**/vendor/**/*)",
    "Read(./packages/legacy-auth/**/*)"
    ]
    }
    }

    Relative patterns anchor at the directory you start claude from, so the last rule protects sessions started at the repository root. If you start sessions from a package, write repo-wide rules as //-absolute paths, for example Read(//abs/path/to/repo/packages/legacy-auth/**/*).

  4. Replace file scans with a language server. A code intelligence plugin lets Claude jump to definitions and find references instead of reading files to locate a symbol, and it reports type errors after edits. Install the language server binary first, then the plugin, inside a session. For TypeScript:

    Terminal window
    # terminal
    npm install -g typescript-language-server typescript
    /plugin install typescript-lsp@claude-plugins-official

    The official marketplace also carries pyright-lsp, gopls-lsp, rust-analyzer-lsp, jdtls-lsp, and other language plugins (listed in anthropics/claude-plugins-official on 2026-09-26). To enable a plugin for the whole team, add it to enabledPlugins in the committed .claude/settings.json. If your organization already runs a code search or RAG index over the repository, expose it as an MCP server so Claude queries the index instead of reading files; see code intelligence MCP servers.

  5. Grant sibling access explicitly. A session started in packages/api/ cannot edit packages/shared/ until you allow it. Commit the siblings every API developer needs, or pass --add-dir at launch (or run /add-dir in a session) for a one-off:

    {
    "permissions": { "additionalDirectories": ["../shared"] }
    }
    Terminal window
    # terminal, from packages/api
    CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 claude --add-dir ../shared

    A directory added through additionalDirectories never loads its CLAUDE.md, rules or skills. One added with --add-dir loads its skills, and loads its CLAUDE.md and rules only when that environment variable is set.

Package-specific procedures that are too long for CLAUDE.md, such as how to write API tests, belong in a skill under packages/api/.claude/skills/. Skills load only when Claude judges them relevant, so API tooling costs nothing during frontend work.

Explore a large area without filling the main context

Section titled “Explore a large area without filling the main context”

Before you change anything that crosses packages, get an inventory. Run the search in a subagent so hundreds of file reads stay out of your main conversation, and name the indirection your codebase uses, because text search finds imports but not handlers registered by string key or dependency injection.

Run the rg command it gives you and compare the count with the table. A mismatch means the inventory is incomplete; ask Claude to explain each missing hit before you plan. For a full onboarding map of an unfamiliar repository, see understanding an unfamiliar codebase with Claude Code.

Make a cross-package change in one planned session

Section titled “Make a cross-package change in one planned session”

A change to a shared type and all its call sites belongs in one session, so every edit follows the same decisions. Plan it first: in plan mode Claude writes the plan to a file, and Claude Code re-injects that plan after each compaction, so a long session keeps its plan when older conversation history is summarised.

Read the plan, not the code. Check the edit order, the uncovered call sites and the “files outside this list” line. Add tests for the uncovered call sites before approving, or accept the gap in writing.

If the change splits into independent units that can each merge on its own, such as the same migration in 30 packages, hand it to /batch instead; see batch operations across hundreds of files.

Two sessions in the same checkout share one working tree, so the second can overwrite the first. Give each parallel session its own git worktree:

Terminal window
# terminal, one per task
cd packages/api && claude --worktree api-rate-limit
cd packages/web && claude --worktree web-settings-page

In a large repository a full worktree is slow to create and heavy on disk. Commit worktree.sparsePaths so each worktree checks out only the listed directories plus root-level files, and symlinkDirectories so node_modules is linked instead of copied. Paths are relative to the repository root. Include .claude so the root settings and rules exist inside the worktree. Put sparsePaths and symlinkDirectories in the .claude/settings.json of the directory you launch from (here packages/api/); they are read before the worktree exists.

{
"worktree": {
"sparsePaths": [".claude", "packages/api", "packages/shared"],
"symlinkDirectories": ["node_modules"]
}
}

Two rules keep parallel work mergeable. First, one owner per file: if two tasks must both change packages/shared, sequence them instead of running them side by side. Second, inside a worktree the session reads the worktree root’s .claude/settings.json, not the package’s, so keep a copy of your Read deny rules in the repository root’s settings. For supervising many sessions at once, see running many agents in parallel with worktrees.

How do you verify a large-codebase change without reading every diff?

Section titled “How do you verify a large-codebase change without reading every diff?”

Prove three things mechanically, then spend human attention only on the plan and on the files the checks flag.

QuestionCheckPasses when
Did the change stay in scope?git diff --name-only against the plan’s file listEvery changed file is in the plan, or has a stated reason
Is it complete?An rg count of the old patternThe count is 0
Does every consumer still work?Typecheck and tests on changed packages and their dependentsGreen on all of them, not only the package you edited
#!/usr/bin/env bash
# verify-change.sh: run from the repository root after the session finishes
set -euo pipefail
base=origin/main
echo "== Files changed (compare with the plan)"
git diff --name-only "$base"...HEAD
echo "== Positional authenticate( calls left (must be 0)"
# rg exits 1 when nothing matches, which is the passing case, so || true keeps set -e and pipefail from aborting
count=$( { rg -nP 'authenticate\((?!\s*\{)' packages/ --type ts --glob '!packages/legacy-auth/**' || true; } | wc -l)
echo "$count"
test "$count" -eq 0
echo "== Typecheck and test every changed package and its dependents"
pnpm --filter "...[$base]" run typecheck
pnpm --filter "...[$base]" test

Adjust the pattern to your call style: exclude the file that defines UserService (for example --glob '!packages/shared/src/user-service.ts') and any unrelated authenticate( APIs. The language server’s reference list is the tie-breaker.

The ...[origin/main] filter selects packages changed since origin/main plus every package that depends on them, which is where a signature change breaks. Nx (nx affected -t typecheck test) and Turborepo (turbo run typecheck test --filter=...[origin/main]) have the same idea. Run the same script in CI so the gate does not depend on anyone remembering it.

Then run /code-review on the branch for a second pass focused on correctness, and route the pull request through CODEOWNERS so the owner of each touched package signs off on their slice. The owner reviews the plan and the gate output for their package, not every line. For the review and CI side, see review automation for Claude Code.

What breaks in a large codebase, and how do you recover?

Section titled “What breaks in a large codebase, and how do you recover?”

A deprecated package with the same names as the live one (legacy-auth next to auth) wins the search. Recovery: /rewind the edits, add the deprecated path to Read deny rules and claudeMdExcludes, and restart from the live package’s directory. Add one line to the root CLAUDE.md naming the live package.

The caller inventory misses indirect references

Section titled “The caller inventory misses indirect references”

Text search finds imports, not routes registered by string or services resolved through a container. The change compiles and fails at runtime. Recovery: name each indirection mechanism in the prompt, cross-check with rg, rely on the language server’s references where you have one, and add a test for every call site the plan marked as uncovered.

The context fills halfway through a cross-package change

Section titled “The context fills halfway through a cross-package change”

Claude starts repeating work or forgets an earlier decision. Recovery: run /compact with a focus, for example /compact keep the plan, the caller table, and the list of packages already done. The plan file is re-injected after compaction. If quality is still poor, /clear and start a new session that reads the plan and continues from the first unfinished package.

A package’s conventions are ignored after --add-dir

Section titled “A package’s conventions are ignored after --add-dir”

You added ../shared and Claude ignored its CLAUDE.md. Directories added with --add-dir load their CLAUDE.md only when CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 is set, and additionalDirectories never loads it. Recovery: restart with the variable set, or start from the root for this task.

A worktree session reads the root .claude/settings.json, so rules committed only in packages/api/.claude/settings.json do not apply there. Recovery: copy the deny rules into the root settings file.

A long session started from the root can accumulate hundreds of skills from every directory it touches. When there are many, some skills lose their descriptions entirely, so Claude no longer has the keywords it needs to pick the right one. Recovery: start from the package, keep descriptions short and lead with the words a request contains (“writing or modifying tests in packages/api/”), and move shared skills into a plugin.

A CLAUDE.md still describes session-based auth after the move to JWT. Recovery: review CLAUDE.md changes in pull requests like code, run the reconcile prompt above after large refactors, and re-read your instructions after a major model release, since workarounds for an older model become overhead.

How Codex and Cursor handle large repositories

Section titled “How Codex and Cursor handle large repositories”

The scoping principles are the same across tools, but the configuration is not: Codex reads AGENTS.md files and Cursor uses its own rules. For the cross-tool strategy, see strategies for million-line codebases and monorepo workflows with AI assistants. For Cursor’s version of this page, see Cursor large codebase strategies.