Lesson 08: Working with Large Codebases
The Challenge
Claude has a context window limit. A large codebase won't fit. The key skill is helping Claude navigate to exactly the right information without overwhelming the context with irrelevant files.
How Claude Navigates Code
Claude uses several tools to explore your codebase:
| Tool | Use Case |
|---|---|
Glob |
Find files by pattern: **/*.ts, src/api/** |
Grep |
Search file contents: find all uses of a function |
Read |
Read a specific file (or portion of it) |
Task (Explore agent) |
Deep multi-file research |
The better your CLAUDE.md describes the project structure, the less exploring Claude has to do.
Strategy 1: Give Claude the Map
The single best thing you can do for large codebase work is describe the architecture in CLAUDE.md:
## Project Structure
- /src/api — HTTP route handlers (thin layer, no business logic)
- /src/services — Business logic (all domain operations live here)
- /src/db — Database access via Prisma (all queries here, nowhere else)
- /src/lib — Shared utilities (logging, HTTP client, config)
- /src/types — TypeScript interfaces (no implementation)
- /tests — Mirror of /src structure
## Key Entry Points
- API server: /src/server.ts
- Background workers: /src/workers/index.ts
- DB schema: /prisma/schema.prisma
- Environment config: /src/lib/config.ts (reads from .env)
## Important Patterns
- All service methods return `Result<T, AppError>` (never throw)
- Events use the event bus in /src/lib/events.ts
- Auth context is always passed as first argument to service methods
With this map, Claude can go directly to the right files instead of grep-crawling the entire repo.
Strategy 2: Point Claude to the Right Place
Instead of "add user authentication," say:
"Add a
deactivateAccount(userId)method to/src/services/user.service.ts. It should setstatus: 'deactivated'and emit auser.deactivatedevent via the event bus at/src/lib/events.ts. Follow the pattern used bydeleteAccount()in the same file."
You're telling Claude:
- Exactly which file to work in
- Exactly what existing pattern to follow
- Exactly what external systems to use
Strategy 3: Use Sub-Agents for Exploration
For genuine exploration tasks (finding all places a concept appears, understanding an unfamiliar subsystem), ask Claude to use a sub-agent:
"Use the Explore agent to find all places where we handle payment webhook events. Give me a map of which files handle which events before we make any changes."
This keeps the exploration out of your main conversation context.
Strategy 4: Work Incrementally
On large tasks, don't try to do everything in one shot. Break it up:
Bad: "Migrate the entire auth system from JWTs to session tokens."
Good:
- Session 1: "Map out all files that touch JWTs. Don't make any changes yet."
- Session 2: "Create the session store and session middleware. Tests only."
- Session 3: "Update the login endpoint to create sessions instead of JWTs."
- Session 4: "Update the auth middleware to validate sessions instead of JWTs."
- Session 5: "Remove the old JWT code and update tests."
Each session is focused and reviewable.
Strategy 5: Leverage Grep Before Asking
Before asking Claude to find something, you can do a quick Grep yourself and hand Claude the results:
"I ran grep and found that
calculateTax()is called from these 5 files: [list]. Please update all call sites to use the newcalculateTax(amount, region)signature."
This saves Claude the exploration step and focuses it on the actual work.
Strategy 6: Reference by Line Number
When Claude has read a file, reference line numbers to be precise:
"The issue is in the
processPaymentfunction starting at line 234. The problem is on line 251 where it doesn't handle theCARD_DECLINEDerror code."
Claude holds line numbers in context. This is much faster than describing the code textually.
Working with Monorepos
For monorepos, be explicit about which package you're working in:
## Monorepo Structure
This is a Turborepo monorepo.
- /apps/web — Next.js frontend
- /apps/api — Express API
- /packages/db — Shared Prisma client and types
- /packages/ui — Shared React components
- /packages/utils — Shared utilities
When working on a feature, always specify which package(s) are involved.
Run commands from the repo root: `turbo run test --filter=api`
Handling Long Files
For very long files (1000+ lines), Claude can read them in chunks. Help it navigate:
"Read lines 450-600 of
services/billing.ts— that's where the subscription renewal logic lives."
Or:
"The
InvoiceProcessorclass is inservices/billing.ts. Read just that class (it starts around line 450)."
The "Before You Start" Pattern
For any large task, establish shared context first:
"Before we start, read these three files:
services/user.ts,types/user.ts, anddb/users.ts. Tell me what you understand about how users are structured in this system. Then I'll explain what we need to change."
This ensures Claude has the right mental model before it writes a single line.
Practical Exercise
Take a feature in a large project you work on. Instead of just asking Claude to implement it:
- Write out which files are involved (even if you're guessing)
- Describe the pattern Claude should follow (point to an existing similar feature)
- Define the scope explicitly (what files are in-bounds and out-of-bounds)
- Ask Claude to confirm its understanding before writing code
Compare the quality of the result to your previous "just ask and see what happens" approach.