Claude Code on a large codebase: what actually works

Claude Code large codebase tips that work: start in the package, a CLAUDE.md per area, block generated code, subagents for reading, tests as the contract.

Contents

On a large codebase, give Claude Code less to read. Start claude inside the package you're changing (cd packages/server && claude), keep a short root CLAUDE.md plus one per package, deny reads of generated and vendored code, hand searches to subagents, give Claude tests it can run, and keep one task per chat. Anthropic's monorepo guide says the same.

It's also what works in ours: 11 packages under packages/, 21,742 tracked files on 25 October 2026, 8,596 of them in packages/app, the app itself. Most days several agents edit it at once (our root CLAUDE.md: "Parallel agents share this tree."). Anthropic's docs were read on 22 October 2026.

Why Claude Code struggles on a large codebase#

Anthropic's monorepo guide puts it down to the defaults: as a codebase grows, "the defaults tuned for smaller projects can fill the context window with instructions and file reads unrelated to the task, costing tokens and degrading Claude's performance." Sections 1 and 2 deal with the instructions, 3 to 5 with the file reads.

1. Start Claude where the work is#

The folder you run claude from sets which files it may edit and which CLAUDE.md files load at launch.

Start from Can edit CLAUDE.md loaded at launch Use when
The repository root Every file The root one; subfolders load when touched The task spans several packages
A package, e.g. packages/api/ That package, until you grant more That package's plus every parent's The work stays in one package

From packages/api/, claude --add-dir ../shared adds a sibling for one session. To keep it, list the folder under permissions.additionalDirectories in that package's .claude/settings.json. Project settings aren't inherited from parent folders the way CLAUDE.md files are.

2. A short root CLAUDE.md, and one per package#

A single root file that covers every subsystem ends up huge or vague. Anthropic's guide splits it:

  • the root CLAUDE.md, for what applies everywhere, such as commit conventions and how to run package scripts;
  • packages/<name>/CLAUDE.md, for that package's stack, commands and traps.

Anthropic's number is under 200 lines per file; what earns a line is in CLAUDE.md best practices. To skip another team's files, put claudeMdExcludes in .claude/settings.local.json with a glob like "**/packages/web/**". A rule for scattered paths goes in .claude/rules/ with a paths: list, and loads only when Claude touches a matching file.

Our root CLAUDE.md holds the rules every package shares. On 25 October 2026 it was 298 lines, 98 past Anthropic's 200, and we pay for every one of them on every request, so trimming it is on our list. packages/server/CLAUDE.md is 196 lines of the server's build and test commands, from npm run build:server down. Each file has an AGENTS.md symlink next to it, which is how Codex reads the same words.

3. Keep generated and vendored code out#

Anything in .gitignore is already out of Claude's searches: node_modules/, dist/, build/. Generated code you commit is not. In our repo that's 475 Kotlin protocol files, 42,518 lines, under native/android/protocol/. Read deny rules keep such files out:

json
{
  "permissions": {
    "deny": [
      "Read(./**/dist/**/*)",
      "Read(./**/vendor/**/*)",
      "Read(./**/*.generated.*)"
    ]
  }
}

Put it in .claude/settings.json to commit it for everyone, or in .claude/settings.local.json for yourself.

4. Find symbols without reading files#

Ask where resolvePath is defined on a big tree and Claude may run a dozen greps and file reads. A code intelligence plugin answers from a language server instead (go to definition, find references) and shows Claude type errors right after each edit. For TypeScript it's one command:

text
/plugin install typescript-lsp@claude-plugins-official

The official marketplace has plugins for Python, Go, Rust and other common languages. Each needs that language's server installed on your machine.

5. Send exploration to subagents#

Of everything here, this is the habit we lean on most. A prompt such as "Use a subagent to find every caller of resolvePath and tell me which ones pass user input" does its reading in a side context and hands back a short list. The subagent's own requests still count against your usage.

It's Anthropic's advice too, in the best practices: "Delegate research with 'use subagents to investigate X'. They explore in a separate context, keeping your main conversation clean for implementation." Their list of common failures includes "the infinite exploration", an unscoped "investigate this" that reads hundreds of files.

6. Make tests the contract#

Anthropic's line is "Give Claude a check it can run": a test suite, a build, a linter. On a big repo, which check matters. Ours is in capitals in the root CLAUDE.md: "NEVER run the full test suite locally. The test suites are heavy and will freeze the machine, especially if multiple agents run them in parallel." So an agent runs npx vitest run <file> --bail=1 on the file it changed, which is quick enough to run after each edit and prints little enough to read without flooding the context.

7. One task per chat#

  • /clear between unrelated tasks. A session full of old corrections does worse than a fresh one with a better prompt.
  • Tasks that run side by side each get their own session in their own git worktree, so edits don't collide.
  • If claude --worktree makes the worktree, worktree.sparsePaths checks out only the folders a task needs, and symlinkDirectories shares node_modules instead of copying it.

A change that crosses packages, such as a shared type and all its callers, is the exception. The guide says to give Claude the whole change in one session and to plan first in plan mode. Claude Code writes the plan to a file and re-reads it after each compaction, so the plan survives a long session.

Is a bigger context window the fix?#

Fable models, Sonnet 5 and later, Haiku 5.5, Opus 4.6 and later, and Sonnet 4.6 support a 1 million token window (as of 22 October 2026). On a cross-package change with a lot to hold at once, that room helps. Focus is another thing: performance still degrades as context fills, and every token is re-sent on every request. One request at 50K, 200K, 500K and 900K tokens is priced in Claude Code 1M context window.

How we run this in Shuriken#

Shuriken doesn't change how Claude Code reads a repository; everything above is plain Claude Code. Two of the habits are defaults in it:

  • Every chat except the project's Main chat gets its own branch in its own git worktree, so a chat in packages/server and one in packages/cloud never touch each other's files. We usually have several going at once (running Claude Code agents in parallel shows what that does to your limits).
  • Finish runs the checks you list in the project's shuriken.json, then rebases and squash-merges, or opens a pull request.

Each worktree is a full checkout, so on a large repository watch your disk. Ours also runs the worktree.setup steps from our shuriken.json in every new one, starting with npm ci and ending with two package builds.

Sources

  1. Set up Claude Code in a monorepo or large codebase (Claude Code docs) (accessed 22 October 2026)
  2. Best practices for Claude Code (Claude Code docs) (accessed 22 October 2026)
  3. How Claude remembers your project (Claude Code docs) (accessed 22 October 2026)
  4. Explore the context window: 1M context (Claude Code docs) (accessed 22 October 2026)
  5. Manage costs effectively: subagents and code intelligence (Claude Code docs) (accessed 22 October 2026)

Frequently asked questions

Should I start Claude Code at the repository root or inside a package?

Inside the package when the work stays there, for example cd packages/api && claude. It then loads that package's CLAUDE.md plus every parent's, and can edit only that subtree until you grant more. Start at the root when a task spans several packages.

Does Claude Code read every CLAUDE.md in a monorepo?

No. At launch it loads the CLAUDE.md in your working folder and every folder above it. A subfolder's CLAUDE.md loads only when Claude reads or edits a file there, and claudeMdExcludes skips other teams' files for good.

How do I stop Claude Code reading build output and vendored code?

Its searches already skip what .gitignore lists. For generated or vendored code that is checked in, add Read deny rules such as Read(./**/vendor/**/*) to permissions.deny in .claude/settings.json.

Is a 1M token context window the answer for a big codebase?

It helps on a cross-package change, but Anthropic's best practices say model performance degrades as context fills, so scoping what Claude reads matters at 1M tokens too. Compaction works the same way at the larger limit.

Do subagents help on a large codebase?

Yes, for reading. "Use a subagent to find every caller of X" runs the search in a separate context window and brings back a short summary instead of hundreds of file reads. Those requests still count against your usage.

Free tools