Large Codebase Strategies
Large codebases past roughly 100,000 lines break the default Cursor workflow: indexing slows, the agent cannot hold the project in context, and it invents patterns instead of matching existing ones. The fixes are aggressive .cursorignore exclusions, rules that encode what code cannot show, prompts scoped to a handful of files, and short conversations.
You joined a team working on a 300,000-line TypeScript monolith. Cursor’s indexing takes twenty minutes. You ask it to explain the authentication flow and get a vague answer that misses half the middleware chain. You try Agent mode for a feature and it writes a new file from scratch instead of integrating with the service layer three directories down. Tab keeps suggesting patterns from a module that was deprecated two years ago.
The tool that made you faster on small projects is now slowing you down on the project that matters. Nothing here is a Cursor bug — it is what happens when a finite context window meets a codebase that does not fit in it, and every strategy below is about deciding what goes into that window.
What large-codebase strategies give you
Section titled “What large-codebase strategies give you”- An indexing strategy that keeps Cursor fast on codebases over 500k lines
- Context budget management that stops the agent drowning in irrelevant files
- Rules that encode the domain knowledge the AI cannot infer from code alone
- Multi-root workspace configurations for monorepos and multi-repo setups
- Scoped search and navigation that finds the right code in seconds
- Architectural analysis prompts for the parts of the codebase nobody explained to you
Getting indexing right before anything else
Section titled “Getting indexing right before anything else”The first time you open a large project, Cursor indexes everything. If node_modules holds 100,000 files, that is 100,000 files consuming memory and CPU before you write a line of code.
Excluding the noise with .cursorignore
Section titled “Excluding the noise with .cursorignore”Create or update .cursorignore in the project root; it works like .gitignore:
# Build artifactsdist/build/.next/out/coverage/
# Dependenciesnode_modules/vendor/.pnp/
# Generated code*.generated.ts*.generated.js__generated__/prisma/generated/graphql/generated/
# Large binary files*.wasm*.map*.min.js*.min.css*.bundle.js
# Lock files (huge, rarely useful for AI)package-lock.jsonpnpm-lock.yamlyarn.lockThis cuts indexing time, keeps the context window clean, and stops the agent citing generated or vendored code as if it were yours.
Opening packages, not the monorepo root
Section titled “Opening packages, not the monorepo root”In a monorepo with twenty packages, opening the root forces Cursor to index all of them. Open the ones you are actually working in:
# Instead of:cursor ~/projects/big-monorepo
# Open specific packages as a multi-root workspace:cursor ~/projects/big-monorepo/packages/web \ ~/projects/big-monorepo/packages/api \ ~/projects/big-monorepo/packages/sharedFor combinations you use repeatedly, save a workspace file:
// web-api.code-workspace{ "folders": [ { "path": "packages/web", "name": "Web App" }, { "path": "packages/api", "name": "API Server" }, { "path": "packages/shared", "name": "Shared Types" }, { "path": "packages/config", "name": "Config" } ]}Watching indexing health
Section titled “Watching indexing health”Check the status in Settings > Indexing and Docs. Cursor does not publish official index-time or index-size figures, so treat this as a rough rule of thumb from field experience, not as documented limits:
| Codebase Size | Rough Index Time | Rough Index Size |
|---|---|---|
| Under 10k files | 1-3 minutes | Under 500 MB |
| 10k-50k files | 3-10 minutes | 500 MB - 2 GB |
| 50k-200k files | 10-30 minutes | 2-5 GB |
| Over 200k files | Use exclusions aggressively | Keep it lean with exclusions |
If indexing takes dramatically longer than that, the exclusions need work. The usual culprits are node_modules that never got excluded, large generated files, and binary assets sitting in the source tree.
Encoding what the AI cannot infer from code
Section titled “Encoding what the AI cannot infer from code”Think about what you would tell a new hire on their first day. That same context is what .cursor/rules/ is for — the knowledge that is true about the project but not visible in any single file.
A rule that describes how work gets done here
Section titled “A rule that describes how work gets done here”---description: Add a new API endpointalwaysApply: false---
When creating new API endpoints:
1. Define the route in src/routes/[domain]/index.ts2. Create the controller in src/controllers/[domain]/3. Add validation schemas in src/schemas/[domain].ts using Zod4. Register the route in src/app.ts under the appropriate middleware group5. All endpoints must use the withAuth middleware unless explicitly public6. Error responses use our standard format: { error: string, code: string, details?: unknown }
See @src/routes/users/index.ts for a complete example.The @file reference at the end is what turns a description into an example the agent can copy.
Glob-scoped rules that attach themselves
Section titled “Glob-scoped rules that attach themselves”Rules matching a file pattern are included automatically when the agent works on matching files, so backend conventions load only for backend work:
---globs: "src/api/**/*.ts"---
Backend API conventions:- All handlers receive (req: Request, res: Response, next: NextFunction)- Use the logger from @src/lib/logger, never console.log- Database queries go through the repository layer, never raw SQL in handlers- All mutations must be wrapped in transactionsThis is the mechanism that keeps React patterns out of your Express code without anyone repeating the rule in every prompt.
Managing the context budget
Section titled “Managing the context budget”Thinking in budgets
Section titled “Thinking in budgets”Every agent conversation has a finite context window, and in a large codebase you hit it fast. Treat context as money:
- Budget: around 200k tokens per conversation (Cursor’s documented default context window; Max Mode and some models extend it)
- Cost per file: a typical 200-line TypeScript file costs roughly 1,000-2,000 tokens
- Cost per directory: referencing
@src/on a 500-file project spends the entire budget in one move
The discipline is to reference the minimum number of files the task needs, then expand only when the agent says it is missing something.
Summary files instead of whole modules
Section titled “Summary files instead of whole modules”For modules too large to reference directly, write a summary the agent can read instead:
# Payments Module Architecture
## Key Files- `service.ts` - Core payment processing (Stripe integration)- `webhook.ts` - Webhook handlers for payment events- `types.ts` - TypeScript interfaces for payment entities- `repository.ts` - Database operations (PostgreSQL via Drizzle)
## Data Flow1. Client calls POST /api/payments/checkout2. service.ts creates Stripe session3. Stripe sends webhook to webhook.ts4. webhook.ts updates payment status via repository.ts5. WebSocket notifies client of status change
## Key Constraints- All amounts in cents (integer, never float)- Idempotency keys required for all Stripe calls- Webhook verification must happen before processingReferencing @src/payments/ARCHITECTURE.md costs around 2k tokens where @src/payments/ might cost 50k, and you pull in the specific files only once you know which ones matter.
For the project you work in every day, the same trick is worth doing once at the top level:
Pointing at files with @-mentions
Section titled “Pointing at files with @-mentions”In a large codebase the agent needs help finding the right files, and naming four of them costs far less than letting it search:
Add a new user notification preferences endpoint.
Follow the patterns in:- @src/routes/users/index.ts (route definition)- @src/controllers/users/profile.ts (controller pattern)- @src/schemas/user.ts (validation schema)- @src/services/user-service.ts (service layer)
The new endpoint should be PATCH /api/users/notification-preferencesScoping every prompt down
Section titled “Scoping every prompt down”The single most important habit for large codebases: never ask Agent to do too much at once. Instead of “add role-based access control to the application,” break it down:
- “Add a
rolecolumn to the users table and create the migration” (1-2 files) - “Create a
withRolemiddleware that checks user roles, following the pattern in @src/middleware/auth.ts” (1-2 files) - “Apply the
withRole('admin')middleware to the admin routes in @src/routes/admin/index.ts” (1 file) - “Add tests for the role middleware in @src/middleware/tests/role.test.ts” (1 file)
Each step is small enough for the agent to hold every relevant file, and each one builds on committed, verified work.
The scope can also be stated as a boundary rather than a file list, which is the version to reach for when the agent has been wandering:
Navigating code you did not write
Section titled “Navigating code you did not write”Tracing a request end to end
Section titled “Tracing a request end to end”Before changing anything in an unfamiliar area, ask:
Trace the request lifecycle for POST /api/orders/create.
Start from the route definition, through all middleware, into the controller,through the service layer, and into the database queries. List every filein the call chain and explain what each one does.
Include error handling paths -- what happens when validation fails,when the database is unavailable, and when the payment provider rejects.That exploration takes thirty seconds in Ask mode and saves fifteen minutes of manual file navigation.
Semantic search plus grep
Section titled “Semantic search plus grep”Neither search alone is enough at scale. Semantic search finds conceptually related code but happily returns results from a deprecated module; text search finds exact matches but misses the function that does the same thing under a different name. Combine them and exclude the graveyard explicitly:
Find all code that handles subscription renewal. Use semantic searchto identify the relevant modules, then grep for specific function callsto 'renewSubscription', 'processRenewal', and 'handleRenewalWebhook'.Show me results only from the active codebase -- ignore anything insrc/legacy/ or src/deprecated/.When the question spans modules rather than sitting inside one, reach for project-wide semantic search:
@codebase runs project-wide semantic search; if it is not available as a literal mention in your build, the same search is reachable through the @ menu’s codebase and folder search. It is slower than a scoped search and worth it only for genuinely cross-cutting questions.
Building a mental map in thirty minutes
Section titled “Building a mental map in thirty minutes”When onboarding, ask these in order and let each answer inform the next:
- “What are the top-level directories and what is each one responsible for?”
- “Show me the main entry points — where do HTTP requests arrive, and where do scheduled jobs start?”
- “What are the core domain models and how do they relate to each other?”
- “What external services does this application depend on? (databases, APIs, message queues)”
- “Where are the most complex parts of the codebase? Which files have the most dependencies?”
Choosing the right edit surface
Section titled “Choosing the right edit surface”| Tool | Best For | Scope | Speed |
|---|---|---|---|
| Tab | Quick manual edits with AI suggestions | Single cursor position | Instant |
Inline Edit (Cmd/Ctrl+K) | Focused changes to selected code | Single file | Fast |
Agent (Cmd/Ctrl+I) | Multi-file features and refactoring | Multiple files | Thorough |
In large codebases Tab and Inline Edit handle most of your changes. They are faster, more predictable, and never require the AI to understand the whole project. Save Agent for changes that genuinely span files.
Monorepo moves
Section titled “Monorepo moves”Package-specific rules
Section titled “Package-specific rules”Different packages usually have different conventions, and rules live next to the package they govern:
packages/web/.cursor/rules/web.md # React conventionspackages/api/.cursor/rules/api.md # Express conventionspackages/shared/.cursor/rules/shared.md # Pure TypeScript rulespackages/mobile/.cursor/rules/mobile.md # React Native conventionsEditing a file under packages/web/ picks up the React rules; editing packages/api/ picks up the Express ones.
Cross-package changes, step by step
Section titled “Cross-package changes, step by step”When a change in one package forces updates in another, spell out the chain rather than describing the goal:
Refactors with a monorepo-wide blast radius
Section titled “Refactors with a monorepo-wide blast radius”Renaming a shared type touches everything downstream. Give the agent the verification command as part of the task:
Rename the "UserRole" type to "AccountRole" in @packages/shared/src/types.ts.Then find and update every import and usage across all packages. Runpnpm run typecheck at the monorepo root after the rename to verifythere are no broken references.With Auto-Run enabled (Settings -> Cursor Settings -> Agents), the agent does the rename, runs the typecheck, sees the remaining broken references, and fixes them iteratively — as long as pnpm run typecheck is on the Command Allowlist or sandbox mode is on.
Keeping long sessions fast
Section titled “Keeping long sessions fast”- Close files you are not editing. Cursor treats open files as high-priority context, so thirty stale tabs degrade Tab prediction quality, slow agent responses, and hold memory. Use
Cmd+Pto jump to a file instead of keeping it open forever. - Use Max Mode selectively. Extended context costs more. It earns its price for a single file over 3,000 lines, a dependency chain spanning ten files, or planning an architectural change that needs the whole module structure in view. For implementing features, fixing bugs, and writing tests, standard mode is enough.
- Start new chats often. Long conversations accumulate stale context and the agent starts referencing code that no longer exists. Start fresh when you move to a different part of the codebase, when you have committed a set of changes, or after five or six exchanges.
When large-codebase workflows break down
Section titled “When large-codebase workflows break down”Indexing stalls at a percentage and never finishes. Usually one large file is blocking the indexer — a GraphQL codegen output, a Prisma client, a compiled asset that slipped past your exclusions. Add it to .cursorignore and restart the index.
The agent creates files from scratch instead of integrating with existing patterns. The rules are not detailed enough. Add a rule that describes the file structure and points at a canonical example with an @file mention.
The agent answers about the wrong part of the codebase. In a monorepo it confuses similarly named files across packages. Name the package explicitly: “@packages/api/src/users.ts — not the one in packages/web.”
The agent loses the thread partway through a change. The change is too big for one prompt. Break it into smaller committed steps.
The agent follows the wrong conventions. Create glob-scoped rules that auto-attach for the directory in question, so a backend rule loading on src/api/**/*.ts does the work no prompt has to repeat.
Tab suggestions get slow. Close tabs, verify the exclusions are actually applying, and check that no generated directory crept back in. Tab’s latency is proportional to the context it has to process.
Response quality drops mid-conversation. Context saturation. Start a new conversation — in large codebases, sessions should be shorter and more focused than on small projects.
Where to go next with large codebases
Section titled “Where to go next with large codebases”- Context Patterns — the @-mention and context-selection techniques underneath all of this
- Custom Rules and Templates — build a comprehensive rules library
- Performance Optimization — tune indexing and memory for large projects
- Token Management — manage costs when context windows are large