AI Coding Agent on a Codebase With No Tests

No test suite means no safety net for an agent. Pin current behaviour first with characterization tests, then let it work. The staged prompts and the rules.

Steve Jefferson
Steve Jefferson
Developer Advocate
26 August 20261 min read

Running an AI coding agent on a codebase with no tests is the highest-risk way to use one, because the thing that normally catches a bad change does not exist. The fix is not to write a test suite first. It is to pin the current behaviour of the one area you are about to touch, let the agent work inside that pinned area, and expand outward. This guide covers the staged workflow, the prompts that make it work, and the two things you should never let an agent do first in an untested repo.

Why untested code is specifically dangerous with agents

A human editing unfamiliar code moves slowly and gets suspicious. An agent does not. It reads the code, forms a confident theory about what it does, and rewrites it to match that theory. When the theory is right, the result is excellent. When the theory misses an undocumented behaviour that a customer depends on, the code still compiles, still looks cleaner than before, and quietly does something different.

In a tested repo that shows up as a red build. In an untested one it shows up as a support ticket six weeks later, from a customer describing a workflow you did not know existed.

The asymmetry is worth stating plainly: agents increase both the rate of good changes and the rate of undetected ones. Tests are what convert the second group into the first.

Stage 1: pin behaviour before you change anything

You are not trying to test that the code is correct. You are trying to record what it currently does, correct or not. That distinction has a name: a characterization test, coined by Michael Feathers, and also known as golden master or approval testing. You observe outputs for a set of inputs and assert that they stay the same.

That reframing is the unlock for agent work, because it removes the hardest question. The agent does not need to know what the code should do. It needs to record what it does. That is a job agents are unusually good at, and the prompt is deliberately blunt:

text
Read src/billing/invoice.py. Do not change it.

Write characterization tests for calculate_totals() that capture its
current behaviour exactly as written, including behaviour that looks
like a bug. For each test, add a comment saying whether the behaviour
looks intentional or accidental. Cover at minimum:
  - empty and single-item inputs
  - the discount and tax branches
  - every early return
  - null, zero and negative values on each numeric argument

If you cannot determine the output without running the code, say so
instead of guessing.

Two details in that prompt matter more than they look. Telling the agent to capture behaviour that looks like a bug stops it from silently fixing things while it writes tests, which is the classic way a characterization pass turns into an unreviewed change. Asking it to flag intentional versus accidental gives you a reading list: those comments are where the real product decisions are hiding.

Run the tests. Any that fail are wrong about current behaviour, and they are useful, because a test the agent got wrong marks exactly the spot where its theory of the code diverged from reality. That is the region to read yourself.

Stage 2: make the change with the pins in place

Only now does the agent touch production code. The instruction that keeps this safe is a rule about the tests, not the code:

text
Now implement <change> in calculate_totals().

The characterization tests are a fixed contract. Do not edit, delete,
skip or reorder them. If your change makes one fail, stop and tell me
which test, what it asserted, and why the new behaviour is correct.
I will approve the change to the test.

Without that instruction you get the single most common failure in agent-assisted work: the change breaks a test, and the agent updates the test to match the new behaviour. Green build, lost behaviour. It is common enough to have its own write-up in what to do when an AI coding agent changes tests to make them pass, and in an untested repo it is far worse, because the pinned tests are the only record of the old behaviour that exists.

When a pinned test does fail legitimately, that conversation is the valuable part of the whole exercise. The agent has to argue that the old behaviour was wrong, and you get to decide with the evidence in front of you.

Stage 3: expand the pinned area, never the whole repo

The temptation after a good first pass is to ask for full coverage. Resist it. A hundred generated tests across an unfamiliar codebase produces a suite nobody trusts, that takes ten minutes to run, and that everyone eventually starts skipping.

Pin the next thing you are about to change, and nothing else. Coverage grows along the path your work actually takes, which is also the path where it pays off. Over a few months the well-tested parts of the repo end up being exactly the parts that change often, which is the distribution you want anyway. The broader technique for generating tests once you have momentum is covered in using AI to write tests.

Two things to never let an agent do first here

Do not open with a formatting or lint pass. It is tempting because it looks harmless and produces an instantly tidier repo. It also rewrites every file, which destroys your ability to read a meaningful diff for weeks, and buries any real behavioural change in twelve thousand lines of whitespace. Format after the work, in its own commit, or not at all.

Do not open with a dependency upgrade. Upgrades in an untested repo are pure risk with no detection, and the failure surfaces at runtime in whichever code path a customer hits first.

Both are things agents propose enthusiastically and unprompted, because both look like obvious housekeeping. In a repo with a green suite they are fine. Here they spend your entire safety budget before you have written a single test.

Keep the escape hatch obvious

One change per branch, one branch per session, and a commit after the characterization tests pass but before any production code changes. That commit is the point you return to when the theory turns out to be wrong, and it costs nothing to make. The mechanics of getting back there cleanly are in how to roll back a bad AI coding agent change.

Frequently asked questions

Can I just ask the agent to write tests for the whole codebase first?

You can, and the result is usually a large suite that asserts trivia and misses the behaviours that matter. Tests written without a change in mind tend to cover what is easy to test rather than what is risky to break. Pin narrowly, ahead of each change, and the suite stays small and trusted.

What if the code is too tangled to test at all?

Then pin it one level up, at whatever boundary you can actually call: an HTTP endpoint, a CLI command, a queue handler. End to end pins are slower and coarser, and they still catch the change that alters an invoice total. Feathers' own techniques for finding these seams are summarised well at Understand Legacy Code.

Do characterization tests prove the code is correct?

No, and that is the point. They prove behaviour did not change. Correctness is a separate conversation, and one you are much better equipped to have once the current behaviour is written down and visible.

Is it faster to refactor first and test later?

It feels faster for about two days. Refactoring without pins means you cannot tell a successful refactor from a behaviour change, so every review becomes a full read of the original code. The refactoring workflow that does work is in using AI to refactor legacy code, and it starts the same way this guide does.

How did this land?

About the author

Steve Jefferson
Steve Jefferson

Developer Advocate

Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.