Using AI Coding Agents on Large Codebases: Monorepos, Microservices, and Scale

Practical strategies for using AI coding agents effectively on large codebases — from scoping context and navigating monorepos to coordinating changes across microservices.

Using AI Coding Agents on Large Codebases: Monorepos, Microservices, and Scale

AI coding agents work great on small projects. You point Claude Code or Aider at a 20-file Node.js app and it can reason about the entire thing. Ask it to add a feature and it knows where the routes live, how the database is accessed, and which tests to update.

Scale that to a monorepo with 2,000 files across 40 packages, or a microservice architecture with 15 independent services, and the experience changes. The agent gets lost. It reads too many files or too few. It makes changes that break packages it did not know about. It suggests patterns that work in one service but violate conventions in another.

The problem is not model intelligence. The problem is context management. Large codebases have more relevant context than any agent can hold at once, and the skill of working effectively becomes the skill of focusing the agent on exactly the right slice.

This guide covers the practical strategies that make AI agents useful at scale.

Why Large Codebases Break Agent Workflows

Three things go wrong when agents meet large codebases.

Context dilution. When an agent reads 50 files to understand a task that only involves 5, its attention is spread thin. Important details in the relevant files get less weight because the agent is also processing dozens of irrelevant imports, utilities, and configuration files. The result is generic suggestions that miss project-specific patterns.

Convention conflicts. Large codebases, especially older ones, have different conventions in different areas. The auth module uses callbacks. The billing module uses async/await. The API layer uses Express. The admin panel uses Fastify. An agent that picks up patterns from the wrong part of the codebase will produce code that technically works but does not fit.

Dependency blindness. In a monorepo, changing a shared package affects every consumer of that package. An agent working in packages/shared-utils might not realize that its change breaks packages/dashboard and packages/admin. The dependency graph is invisible unless you make it explicit.

Strategy 1: Scope the Working Directory

The simplest and most effective technique is restricting where the agent operates.

Instead of starting an agent session at the monorepo root:

cd ~/monorepo
claude-code

Start it in the specific package or service:

cd ~/monorepo/packages/billing
claude-code

Now the agent's file reads, searches, and edits are naturally scoped to the billing package. It cannot accidentally wander into the auth module and pick up wrong patterns.

For microservice architectures, the same principle applies. One agent session per service:

# Session 1: billing service
cd ~/services/billing-api
claude-code

# Session 2: notification service
cd ~/services/notification-api
claude-code

Each agent sees only its service. Each operates within that service's conventions. No cross-contamination.

When You Need Cross-Package Context

Sometimes the task requires understanding relationships between packages. A change in the shared API types package affects consumers in the dashboard and admin packages.

For these cases, start the agent at the monorepo root but explicitly narrow its focus:

Working in packages/api-types/ and packages/dashboard/src/api/:

The ApiResponse type in packages/api-types/src/responses.ts needs a
new `metadata` field. Update the type definition and all usages in
the dashboard package.

Do not modify any other packages.
Do not modify files outside of the two paths listed above.

After changes, run:
  pnpm --filter api-types build
  pnpm --filter dashboard typecheck

The constraint prevents the agent from chasing the type change into every consumer. You handle other packages in separate, focused sessions.

Strategy 2: Use Project Instructions Per Package

Most AI coding agents support project-level configuration files. In a large codebase, these become essential.

For Claude Code, place a CLAUDE.md in each package or service root:

# packages/billing/CLAUDE.md

## Conventions
- Async/await throughout, no callbacks
- Error handling: throw typed errors from src/errors/
- Database access: use the repository pattern in src/repos/
- Tests: colocated in __tests__/ directories, use vitest

## Key files
- src/services/invoice-service.ts — core billing logic
- src/repos/invoice-repo.ts — database access
- src/api/routes.ts — HTTP endpoints

## Build and test
- pnpm test (runs vitest)
- pnpm build (runs tsc)
- pnpm lint (runs eslint)

For Aider, use a .aider.conf.yml in each directory with relevant conventions.

When the agent starts in packages/billing/, it reads the local configuration automatically. It knows the conventions, key files, and verification commands without you repeating them in every prompt.

This matters more than it seems. On a large codebase, the difference between an agent that knows "use the repository pattern" and one that writes raw SQL is the difference between usable output and a complete rewrite.

Strategy 3: Map Before You Modify

On unfamiliar parts of a large codebase, use the agent for reconnaissance before asking it to make changes.

Before making any changes, help me understand the notification system.

1. What are the main files in packages/notifications/src/?
2. How does a notification get created? Trace the flow from
   API endpoint to database write.
3. What external services does this package depend on?
4. Are there any shared types imported from other packages?

Summarize in a short list. Do not modify any files.

This produces a focused map of the area you are about to work in. You can then give the agent precise instructions based on an accurate understanding of the architecture, rather than hoping it figures things out on its own.

For microservice architectures, the mapping step is even more important:

I need to understand how the order service communicates with
the inventory service.

Check for:
- HTTP calls between services (look for fetch, axios, or got)
- Message queue producers/consumers (look for rabbitmq, kafka, or SQS patterns)
- Shared event schemas or contracts
- Any direct database access across service boundaries

Report what you find with file paths and line numbers.

The agent might discover that the order service calls the inventory service via HTTP in one place and through a message queue in another. That context is critical before making changes.

Strategy 4: One Agent Per Concern

On large codebases, resist the temptation to give one agent session a broad mandate. Instead, run multiple focused sessions.

Bad approach: One agent handling a full feature across the stack.

Add a new "usage reports" feature. Create the database migration,
API endpoints, service logic, frontend components, and tests.

On a small project, this works. On a large codebase with separate packages for API, frontend, and shared types, this prompt leads to an agent jumping between packages, losing track of conventions, and producing inconsistent code.

Better approach: One agent per layer.

# Agent 1 (in packages/api):
Add the usage reports API endpoints in src/routes/reports.ts.
Follow the same pattern as src/routes/analytics.ts.
Add service logic in src/services/report-service.ts.
Add tests. Run pnpm test.

# Agent 2 (in packages/dashboard):
Create a usage reports page at src/pages/reports/.
Follow the same page structure as src/pages/analytics/.
Use the API client in src/api/ to fetch data.
Add component tests. Run pnpm test.

# Agent 3 (in packages/api-types):
Add the UsageReport type definition and API response types.
Follow existing type patterns.
Run pnpm build to verify.

Three agents, three scopes, three independent review cycles. Each agent stays in its lane. You merge the results.

The challenge is managing three concurrent sessions. This is where terminal organization directly affects productivity. Named sessions — one for each concern — with persistent state between context switches keep the workflow manageable.

Strategy 5: Use Git Worktrees for Isolation

When multiple agents work on the same monorepo, file conflicts become a real risk. Git worktrees solve this cleanly.

# Main worktree: agent working on API changes
cd ~/monorepo
git worktree add ../monorepo-api feature/reports-api

# Second worktree: agent working on frontend changes
git worktree add ../monorepo-dashboard feature/reports-dashboard

Now each agent operates in a physically separate copy of the repo on its own branch. No file conflicts possible. When both are done, merge the branches:

git checkout main
git merge feature/reports-api
git merge feature/reports-dashboard

This pattern is especially powerful for large refactors. One worktree per migration target, one agent per worktree, merge when verified.

Strategy 6: Build Checks as Guardrails

Large codebases have build systems, type checkers, linters, and test suites for a reason. Make the agent use them aggressively.

For monorepos with dependency graphs, verify both the changed package and its dependents:

After making changes to packages/shared-utils:

1. Run: pnpm --filter shared-utils test
2. Run: pnpm --filter shared-utils build
3. Run: pnpm --filter "...{packages/shared-utils}" build
   (This builds all packages that depend on shared-utils)
4. Fix any failures before proceeding

That third command is the one most developers miss. It catches the downstream breakage that a local test suite will not.

For microservices, the equivalent is running contract tests or integration tests after changing a service's API:

After modifying the inventory service API:

1. Run unit tests: pnpm test
2. Run contract tests: pnpm test:contracts
3. Verify the OpenAPI spec is still valid: pnpm validate:api
4. Check that the order service client still compiles:
   cd ../order-service && pnpm typecheck

Practical Monorepo Workflow: End to End

Here is a complete workflow for adding a feature to a monorepo with 30+ packages.

Phase 1: Understand. Start one agent at the root. Ask it to map the relevant packages and their relationships. No changes.

Phase 2: Plan. Based on the map, decide which packages need changes. List the order of operations: types first, then API, then frontend.

Phase 3: Execute types. Start an agent in the types package. Make the type changes. Build and verify.

Phase 4: Execute API. Start an agent in the API package. Reference the new types. Implement endpoints and tests. Verify including downstream builds.

Phase 5: Execute frontend. Start an agent in the frontend package. Use the new API client. Implement UI and tests. Verify.

Phase 6: Integration. Run the full monorepo build and test suite. Fix any integration issues with a fresh agent session, giving it the build output as context.

Six phases, each scoped and verifiable. The whole process might use 4-5 agent sessions across 3-4 terminal sessions. That sounds like a lot of management overhead until you compare it to the alternative: one agent session that produces a 500-line diff across 12 packages with no clear order and several subtle breakages.

The Scaling Principle

AI coding agents do not automatically get worse on large codebases. They get worse when given too much context and too little direction.

The fix is always the same: narrow the scope, make the conventions explicit, verify at each step, and use multiple focused sessions instead of one sprawling one.

This is not unique to AI agents. It is how experienced engineers work on large systems too. They do not try to hold the entire codebase in their head. They focus on the relevant slice, understand the local conventions, make a change, verify it, and move on.

AI agents are the same. Treat them like skilled colleagues who need clear scope and good context to do their best work. At scale, that means investing a few minutes in setup — working directory, project instructions, explicit constraints — to save hours of review and rework.


Managing multiple agent sessions across a large codebase? Agents UI provides project-based session organization with persistence, so you can run parallel agents without losing track of which is which.

Try Agents UI

A native terminal for AI coding agents with persistent sessions, SSH workflows, and built-in editing.