AI/TLDR

Using AI Coding Agents in Large, Existing Codebases

Learn why AI agents that dazzle on demos struggle in big existing repos, and the scoping, context, and convention tactics that make them productive at scale.

INTERMEDIATE12 MIN READUPDATED 2026-06-13

In plain English

Most demos of an AI coding assistant show it building a to-do app or a small game from scratch. The whole project fits in a few files, there is no history, and nothing else depends on the code. The agent looks like magic. Then you point that same agent at a real codebase — hundreds of thousands of lines, ten years of decisions, modules nobody fully remembers — and it suddenly feels lost. It edits the wrong file, breaks a feature on the other side of the repo, or invents a helper that already exists three folders over.

AI in Large Codebases — illustration
AI in Large Codebases — dz2cdn1.dzone.com

Here is the everyday analogy. A brilliant contractor can build a garden shed in a weekend because a shed has no plumbing, no neighbours, and no building it has to match. Hand that same contractor a key to a 40-storey office tower and say "add a new break room on floor 23," and the skill is no longer the bottleneck. Now they need the floor plans, they need to know which walls are load-bearing, which pipes run behind them, and which contractor's conventions the rest of the building follows. An AI coding agent in a big repo is exactly that contractor: still talented, but useless until you give it the floor plans and fence the job to one room.

So this article is not about whether agents work — they do. It is a workflow playbook for the specific situation where the code already exists, is large, and matters. The skill of the model is mostly fixed; what you control is how you scope the task, what context you hand over, and how you review the result. That is where success or failure actually lives.

Why it matters

A greenfield app and a mature codebase are different problems, not different sizes of the same problem. Three things change once a repo gets big, and each one breaks the naive "just describe what you want" approach.

  • The code no longer fits. A model only reasons about what is in its context window. A small app fits whole; a large repo does not come close. The agent sees a sliver and has to guess about the rest — and guessing in a codebase means hallucinated APIs, duplicated logic, and edits that ignore code it never read.
  • The conventions are invisible. Every mature project has unwritten rules: this is how we handle errors, this is the logging wrapper everyone uses, this folder is deprecated, never call that function directly. None of it is in the model's training data, and most of it isn't even in comments. An agent that can't see the convention will cheerfully violate it.
  • Changes are cross-cutting. In a toy app, a function has one caller. In a real system, one function is used in forty places, behind a feature flag, with a public type that another team imports. A change that looks local is actually global, and the agent has no way to know unless something tells it.

The cost of getting this wrong is not just a wasted prompt. An agent let loose on a big repo with a vague instruction can produce a 60-file diff that looks plausible, passes a quick glance, and quietly breaks production. Reviewing that mess takes longer than writing the change by hand — which is the exact opposite of why you reached for an agent. The whole point of this playbook is to keep the agent in the zone where it is faster than you, and out of the zone where it just generates work for you to clean up.

How it works

The core loop of every coding agent is the same: it reads some files, decides on an edit, writes it, runs a command (tests, build, linter), reads the result, and repeats until it thinks the task is done. In a large codebase, the only step that really changes is the first one — what the agent reads before it acts. Get the right files and the right rules into the context, and the rest of the loop largely takes care of itself.

Why scale specifically hurts each step

Modern agents don't load the whole repo — they explore it on demand, the way you would: grep for a symbol, open the file it lives in, follow imports, read the tests next to it. That is exactly the right strategy, but it has a ceiling. The agent can only follow trails it knows to look for. If the relevant logic lives in a file with a misleading name, or the convention lives in a doc the agent never opens, the trail goes cold and the model fills the gap with a guess.

So your job is to shorten the trails. Instead of hoping the agent stumbles onto the right module, you point at it. Instead of hoping it infers your error-handling style, you write it down where the agent will always read it. The diagram below shows the two levers you actually pull — scope (how much of the repo is in play) and context (what guidance comes along with the task).

Notice what is not on that diagram: a bigger model or a cleverer prompt phrasing. Those help at the margins, but they don't fix a task that is too broad or a context that is missing the one file that mattered. Scope and context do the heavy lifting.

Scope the task to one area at a time

The single highest-leverage habit is to make the task small and bounded. "Migrate our whole API to the new auth system" is a project, not a prompt. "In src/auth/session.ts, replace the deprecated validateToken call with verifySession, and update the three call sites in src/api/" is a task an agent can nail.

Smaller scope helps on every axis at once: the relevant code fits in context, fewer hidden dependencies are in play, the diff is small enough to actually review, and if it goes wrong you revert one commit instead of untangling a sprawl. The instinct to save time by asking for everything in one shot is the most common way people make agents less productive on big repos.

Prefer many small reviewed changes over one giant rewrite

A 5-file diff you understand and merge is worth more than a 50-file diff you can't trust. Break a big effort into a sequence of small, independently reviewable steps. Let the agent do step one, you review and commit, then start a fresh, clean task for step two. This keeps the context focused, makes regressions easy to bisect, and means a bad step costs you ten minutes, not a day. It is also why spec-driven development — agreeing on the plan in writing before the agent codes — pays off so much more on large projects than on demos.

Give the agent the right context

Once the task is scoped, the second job is feeding the agent what it can't infer. There are two kinds of context: task-specific (which files and patterns are relevant this time) and project-wide (the conventions that are true every time).

Point at the right files and patterns

Don't make the agent search blind. Name the files it should look at, and — even better — point at an existing example of the pattern you want. "Add a new endpoint for /orders, following the same structure as the existing /users endpoint in src/api/users.ts" gives the model a template to copy your conventions from, instead of inventing its own. Pointing at one good example is often worth more than a paragraph of instructions.

Lean on a project context file

Most agents read a special file from the repo root on every run — CLAUDE.md for Claude Code, equivalent rules files for other tools — and treat it as standing instructions. This is the single best place to encode the invisible conventions: the commands to build and test, the libraries you prefer, the patterns to avoid, the folders that are deprecated. Written once, it ships to every future task automatically. Think of it as onboarding docs for a teammate who has perfect recall but zero memory of last week.

a small slice of a project context filetext
## Conventions
- Use the `db.withTransaction()` wrapper for all writes — never call the
  pool directly.
- All API errors go through `AppError`; do not throw raw Error in handlers.
- `legacy/` is frozen. Read it for reference, never edit it.

## Commands
- Test:  npm run test
- Types: npm run typecheck   # MUST pass before a change is done
- Lint:  npm run lint

Make the tests do the talking

Existing tests are the agent's best friend in a large repo. They define correct behaviour precisely, they catch the cross-cutting break the model couldn't see, and they give the agent a fast feedback loop to fix its own mistakes. Tell the agent to run the relevant tests after every change and keep going until they pass. A repo with good test coverage is dramatically safer to hand to an agent than one without — the tests are the guardrail that catches the 40th caller you forgot about.

Common pitfalls in big repos

PitfallWhat goes wrongThe fix
The mega-prompt"Refactor the whole module" yields a huge, unreviewable diffSplit into small, file-scoped steps; review and commit each
Blind searchAgent guesses at file locations and edits the wrong layerName the files; point at an existing example to copy
No standing rulesAgent reinvents helpers and ignores your conventionsEncode conventions in a CLAUDE.md-style project file
Skipping testsA local-looking change breaks a far-off caller silentlyMake the agent run tests every loop; fail the task if they fail
Rubber-stamp review60-file diff looks fine at a glance, ships a bugReview like a real PR; smaller diffs make this possible
Letting it roam freeAgent edits frozen/legacy code or touches unrelated areasScope tightly; use agent permissions to fence it

Going deeper

Once the basics — tight scope, rich context, small reviewed diffs — are second nature, a few more advanced moves help on genuinely large systems.

Retrieval and codebase indexing. Some tools build a searchable index of the whole repo (often using embeddings) so the agent can find relevant code by meaning, not just by grepping for an exact string. This is essentially RAG applied to your source tree, and it helps the agent locate the right files in a repo too big to browse. It is not a substitute for scoping, but it makes the agent's own search far better.

Explore-then-edit as two phases. For an unfamiliar area, run the agent in a read-only pass first: ask it to map out how a feature works and which files are involved, before it changes anything. You review its understanding, correct it, and only then let it edit. This catches a wrong mental model early — when fixing it is a sentence, not a revert.

Connect external context with MCP. The Model Context Protocol lets an agent reach beyond the repo — your issue tracker, design docs, database schema, or internal knowledge base. In a large org, the context that makes a change correct often lives outside the code, and wiring those sources in turns guesses into facts.

Know when not to use an agent. Some changes are still better by hand: a subtle one-line fix in code you understand deeply, a change that hinges on tacit business context that isn't written down anywhere, or a refactor across a poorly-tested area where you can't trust the safety net. The mature skill is not "always use the agent" but knowing which tasks fit. For the prompting craft that makes the in-scope tasks land, see how to prompt coding agents.

The durable lesson: in a large existing codebase, the model's raw ability is rarely the limiting factor — the limiting factor is how much of the right context reaches it and how tightly the task is bounded. Demos hide this because tiny apps have no hidden context to lose. Real engineering with agents is, more than anything, the discipline of feeding them the floor plans and fencing the job to one room.

FAQ

Why do AI coding agents struggle in large codebases when they ace demos?

Demos use tiny apps that fit entirely in the model's context window, have no hidden conventions, and no cross-cutting dependencies. A real repo is too big to fit, full of unwritten rules the model can't see, and full of code where one change ripples across dozens of callers. The agent's skill doesn't drop — it just can't see enough of the system to make safe changes without help.

How do I give a coding agent context about a big existing project?

Two layers. Per-task, name the exact files to look at and point at an existing example of the pattern you want copied. Project-wide, put your conventions, build/test commands, and "never touch this" rules in a standing context file like CLAUDE.md that the agent reads on every run. Good test coverage is the third piece — it lets the agent catch and fix its own mistakes.

Should I ask an AI agent to refactor a whole module at once?

Usually no. A giant refactor produces a sprawling diff you can't realistically review, and one hidden mistake can break code far away. Break it into small, independently reviewable steps: let the agent do one, you review and commit, then start a fresh task for the next. Many small reviewed changes beat one big rewrite on a large repo almost every time.

Can I use Claude Code on a large project?

Yes — large repos are a core use case. The keys are to add a CLAUDE.md with your conventions and commands, scope each task to one area, point Claude at the relevant files, and let it run your tests after each change. Use permissions to keep it out of frozen or unrelated code, and review every diff like a real pull request.

How do agents find the right code in a repo too big to read?

They explore on demand — searching for symbols, opening the files they appear in, and following imports and nearby tests, the way a developer would. Some tools also index the whole codebase with embeddings so they can search by meaning. Both work better when you shorten the trail by naming the relevant files yourself instead of making the agent guess where to start.

Is it safe to let an AI agent edit a legacy codebase?

It can be, with guardrails. Scope tasks tightly, mark frozen folders as off-limits in your context file, require the agent to run the test suite after every change, and review each diff before merging. The risk isn't the agent's intent — it's a confident-looking change that quietly breaks a caller it never saw. Tests and small diffs are what keep that in check.

Further reading