Back to guides

How Do I Keep Claude Code Lean in a Large Monorepo?

Jake McCluskeyAdvanced40 min read
How Do I Keep Claude Code Lean in a Large Monorepo?

The first time you run Claude Code on a real monorepo, the context usage chart looks like a Black Friday traffic graph. A single "explain how this feature works" question pulls in 40 files, and you're staring at a 150k-token conversation before Claude has typed a word. Here's how to keep Claude Code useful on a huge codebase without eating through your token budget — or your patience.

Why this matters

Claude Code is a great collaborator on small projects and a great collaborator on big projects if you configure it correctly. Out of the box, it defaults to reading liberally: the file you referenced, its imports, related test files, CLAUDE.md, and whatever else it thinks relevant. That's the right default on a 50-file project and the wrong default on a 50,000-file monorepo.

With the right configuration — scoped attention, targeted rules, aggressive context management — Claude Code stays precise on a monorepo. You get the speed you want without the token bill you don't.

Before you start

You need:

  • Claude Code 1.5+ installed and working on a large repo.
  • A monorepo you actually work in. The guide makes more sense with a concrete codebase to configure against.
  • 30 minutes. Mostly in config files.

Step 1: Understand what Claude reads by default

Run /context in an active session. Claude shows you exactly what's in its current window:

  • CLAUDE.md files (root + any parent/subpath).
  • Files you've mentioned or Claude has opened.
  • Tool definitions.
  • The conversation so far.

Look at the line counts. On a monorepo, you'll often see dozens of files loaded from "looking around," many irrelevant to the task. That's the problem to fix.

Step 2: Scope with a monorepo-aware CLAUDE.md

At the repo root, your CLAUDE.md should be small — only truly repo-wide rules. Monorepo context belongs in sub-path CLAUDE.md files:

text
/CLAUDE.md              # Repo-wide conventions, very short
/apps/web/CLAUDE.md     # Next.js app conventions
/apps/api/CLAUDE.md     # API server conventions
/packages/ui/CLAUDE.md  # Shared UI package conventions

Claude walks the directory tree from the file it's editing, reading every CLAUDE.md along the way. So when you're editing in apps/web/, Claude reads apps/web/CLAUDE.md + CLAUDE.md — not apps/api/CLAUDE.md.

This alone cuts CLAUDE.md overhead by 60-70% in a typical monorepo.

Step 3: Configure an ignore list

Claude Code respects a .claudeignore (or equivalent — check your version's name). Use it like .gitignore:

text
# .claudeignore
node_modules/
dist/
.next/
coverage/
*.lock
*.min.js
pnpm-lock.yaml

# Monorepo apps you're not touching right now
apps/legacy-dashboard/

Anything in .claudeignore is invisible to Claude — no reads, no greps, nothing. Scoping to the app you're actually working in is the biggest single token win.

For per-session scoping, some versions let you pass --project-dir apps/web to launch Claude Code already scoped to that sub-app. If available, use it for narrow-scope sessions.

Step 4: Avoid open-ended exploration prompts

Prompts like "give me an overview of this codebase" are monorepo budget-killers. Claude will try to read dozens of files to answer, costing you tokens and rarely producing insight you didn't already have.

Replace with scoped questions:

  • "How does apps/web/src/app/api/checkout/route.ts handle the webhook signature?"
  • "Find all uses of useAuth() in apps/web/ and list them."
  • "Summarize the changes in the last 3 commits that touched packages/ui/."

Specific location + specific question = a small, focused read. Claude answers in a few file reads instead of forty.

Step 5: Use /compact or /clear aggressively

Claude Code has commands to reduce the live context:

  • /clear — wipes the conversation history, keeping only CLAUDE.md and current task. Use between distinct tasks.
  • /compact — asks Claude to summarize the current conversation into a shorter form, replacing the verbose history. Use mid-task when context is getting heavy.

Rule of thumb: if a conversation has 30+ turns of tool use, /compact. If you're starting a new task (different feature, different app), /clear.

Step 6: Scope tool use with rule files

For very large repos, limit what Claude can read via permission rules. In .claude/settings.json (or your version's config path), restrict Read and Glob to paths you're working in:

json
{
  "permissions": {
    "allow": {
      "Read": ["apps/web/**", "packages/ui/**", "CLAUDE.md"],
      "Glob": ["apps/web/**", "packages/ui/**"]
    },
    "deny": {
      "Read": ["apps/legacy-*/**"]
    }
  }
}

Now Claude physically cannot read the parts of the monorepo you're not working in. Safer and cheaper.

Check your Claude Code version's docs for the exact permissions format — it's stabilized but the key names can shift.

Step 7: Cache what's stable

For long sessions in a big repo, the initial context (CLAUDE.md + tool definitions) is stable while the conversation grows. That's a prompt caching opportunity — see How Do I Keep My Claude Prompt Cache Hit Rate High? — though Claude Code handles caching under the hood on supported models. Verify in your session logs that cached reads are happening; if not, update to the latest Claude Code.

Step 8: Build a "starting prompt" that skips orientation

Instead of letting Claude figure out where to start on every task, give it a preamble:

text
/clear
We're working in apps/web/, a Next.js 16 app. The feature I'm building is the
Stripe webhook handler at apps/web/src/app/api/webhooks/stripe/route.ts. It
currently handles checkout.session.completed; we're adding invoice.paid.
Related files: apps/web/src/lib/stripe.ts (client), apps/web/prisma/schema.prisma
(Subscription model). Start by reading those three files.

You've done the orientation Claude would have paid 20 file reads to do. The task proceeds from a small, correct initial context.

Verify it worked

1. Cold-start context is small. Open a fresh Claude Code session, type /context. Line count should be under a few thousand — mostly CLAUDE.md and tool definitions, not app code.

2. Tasks finish with fewer file reads. A medium task that used to take 40 Read tool calls should now take 10-15. Watch the tool-use indicators.

3. Cost per session drops. If you're on API billing, compare a typical session's cost before and after configuration. Target: 50%+ reduction.

Where this breaks

  • Ignoring files you actually need. A .claudeignore that excludes packages/shared/ means Claude can't see your shared utilities, which are probably relevant. Start permissive, tighten as you notice over-reads.
  • Stale sub-path CLAUDE.md files. Conventions evolve; sub-path CLAUDE.md files get stale fast because nobody looks at them unless they're actively working there. Add a reminder in root CLAUDE.md: "Sub-path CLAUDE.md files exist at X, Y, Z — keep them current."
  • Claude hallucinating about code it can't see. When you restrict reads aggressively, Claude sometimes confidently describes code it didn't actually read. Ask "show me the line you're referring to" — if Claude can't cite, it was guessing. Widen the scope if this happens often.
  • Per-app permissions drift from actual app structure. Adding a new sub-app that isn't in the allowlist means Claude can't see it on day one. Update .claude/settings.json when you add apps, same time you update other repo-level configs.
  • Compact losing important context. /compact is a summary, not a lossless compression. Details in the compacted portion can disappear. Don't compact right before a precision task; compact after.

What to try next

Want this built for you instead?

Let's talk about your AI + SEO stack

If you'd rather skip the how-to and have it shipped for you, that's what I do. Start a conversation and we'll figure out the fastest path to results.

Let's Talk
Questions from readers

Frequently asked

Where should I put CLAUDE.md files in a monorepo?

Small root CLAUDE.md with repo-wide rules only. Sub-path CLAUDE.md files (apps/web/CLAUDE.md, packages/ui/CLAUDE.md) for app- or package-specific conventions. Claude walks the tree from the file being edited and merges the stack — so you pay for only the relevant files' rules.

Does .claudeignore work the same as .gitignore?

Similar syntax, different purpose. .claudeignore tells Claude what to skip when reading/searching, not what to exclude from git. You can (and often should) include things in .claudeignore that are tracked in git — large generated files, lockfiles, legacy apps — just to keep them out of Claude's working view.

What's the single biggest token win?

Ignoring the apps you're not touching. In a typical monorepo, you work in one app at a time; everything else just dilutes Claude's attention. A .claudeignore that excludes the other apps cuts token usage 50-70% in most sessions.

Should I use /compact or /clear between tasks?

/clear for distinct new tasks — different feature, different app. /compact mid-task when context is growing heavy but you need to keep the thread. Running /compact right before a precision task is risky; details may get lost in the summary.

How do permissions help with cost?

Scoped Read and Glob permissions prevent Claude from reading outside your current work area. Even a well-intentioned 'let me check if there's a pattern elsewhere' becomes a no-op, which is what you want in a monorepo. Sessions stay tight; token usage is predictable.