Making an AI agent useful on a legacy codebase
Legacy code exposes missing context, weak tests and model limitations. A practical way to make small agent-assisted changes more verifiable.
AI coding tools demo beautifully on a new project and disappoint on a fifteen-year-old system with four hundred tables and a directory called utils2.
Legacy systems can be difficult for both people and agents: missing context, multiple patterns, limited tests and model limitations can all contribute. Better documentation helps, but does not make every task safe to delegate.
The four things that actually break
When several generations of a pattern coexist, an agent may imitate one you no longer want. Mark the current approach and explain where older behavior must remain compatible.
Deliberate oddities. An unusual retry or duplicated field may be a workaround with a real requirement behind it. An agent can remove it incorrectly; record the reason and test the behavior before refactoring.
Among the conditions that make coding assistance harder to verify, a slow or unreliable test suite makes iteration harder. Agents may still run it, but you need to budget the time, provide the environment and check that it actually completed.
Undocumented conventions. Things everybody on the team knows and nobody has written down. Which service owns writes to a table. Why you never call that endpoint directly. Which environment variable is load-bearing. A human absorbs these in their first month by being corrected. An agent has no first month.
Start with verification, not with the tool
The highest-value preparation has nothing to do with AI.
Add a fast, focused check for the changed behavior, then retain the broader integration and regression checks the risk requires. A handful of fast tests cannot establish correctness across the whole system.
Count verification and review time in the experiment. If understanding and repairing the generated change takes more effort than the benefit it delivers, narrow the task or change the approach.
Write down which pattern is current
Naming the current pattern can resolve a specific ambiguity. Confirm the agent uses it correctly rather than assuming the documentation alone fixes the problem.
One line per area in your instructions file: “New endpoints follow api/v2/orders.py. Do not copy api/v1.” “Use the repository classes; direct SQL in handlers is legacy.”
Point to a reviewed reference implementation and keep that reference current. Code examples can drift too; explain which parts are conventions and which are incidental.
Work outward from the parts that are already good
The instinct is to point these tools at the worst code, because that is where the pain is. That is backwards.
The worst code is the least legible, least tested and most surprising — the conditions under which an agent performs worst and a mistake costs the most. Start where the code is boring and well covered. Build a sense of where the tool is reliable. Expand outward as you learn its failure modes on your system, which are not the same as its failure modes in general.
An agent can draft regression tests, but check the expected behavior independently. A test that merely reproduces the implementation’s mistake gives false confidence; use a known bug, requirement or independently calculated result as the oracle.
Give it the history
Intent may live in code, tests, commit history, incident records or a colleague’s knowledge. Ask where the relevant invariant is documented before changing behavior that looks unusual.
This also protects against the tidying failure. An agent that can see a line was added in a commit titled “fix intermittent duplicate charges” is considerably less likely to helpfully simplify it away.
Scope the task smaller than feels necessary
The instinct on old code is to ask for the whole change, because that is what you would ask a colleague who knows the system. It is the wrong instinct here.
For example, scope a rate-limiting change to named middleware and endpoints, cite the current pattern, and require tests for expected and rejected requests. Estimate review time from the actual change, not from the prompt’s specificity.
For a change like this, review the relevant rate-limiting behavior and tests before accepting the patch. Naming the constraint can save rework, but the time saved has to be measured rather than assumed.
Watch for the two expensive failure shapes
The confident wrong refactor. An agent tidies something it did not understand, the change looks like an improvement, and it passes review because it reads well. This is why comments on deliberate oddities are worth more than they look, and why the areas with weakest tests are the worst place to start.
The plausible diff in an untested area. Code that looks right, changes behavior subtly, and has nothing to catch it. Everything in this post about verification exists to prevent this one, because it is the failure that damages trust in the whole approach after a single incident.
Review both the selected pattern and the preserved invariant, and test where possible. A careful reading may miss concurrency, integration or runtime failures.
What stays a human job
Being honest here prevents disappointment and, more importantly, prevents somebody committing to a plan that cannot work.
Anything requiring knowledge that exists only in people’s heads. Anything where the blast radius is large and the verification is weak — which describes a lot of the most valuable legacy work. Anything where the right answer is “this should not be changed, it should be replaced, and that is a six-month conversation”.
Try a better-fitting tool or model when the evidence supports it, but do not expect a tool switch to repair missing requirements or an unusable test environment.
The part that pays off either way
Notice that almost everything above — fast tests, a written record of the current pattern, comments on deliberate oddities, conventions out of heads and into files — is work that makes the codebase better for the people on your team.
That is the useful framing. If AI tooling changes completely in eighteen months, you have still made your system more legible, and legibility is the thing that determines how fast anyone can safely change it. Treat the agent as a demanding new colleague who exposes exactly where your codebase fails to explain itself, and the preparation is worth doing on its own merits — which is a considerably safer bet than a preparation that only pays off if a particular tool keeps working.
Technical review, October 10, 2026. References: Claude Code: project instructions; GitHub: Copilot Agents application card.
Try this
Give an agent one small change in a well-tested area on a disposable branch. Record failures and investigate their causes before editing instructions. Check the final behavior and review effort; the result is evidence about this task, not the whole codebase.