Playbook
Refactoring legacy code with an AI agent feels like a superpower right up until the moment the agent "cleans up" a function that three other systems quietly depended on, and now production is down and nobody can say why. The problem isn't the model. The problem is that legacy code is a minefield where the mines are undocumented behaviors, and an agent moving fast through a minefield is worse than a human moving slow. This is the playbook we use to refactor legacy systems with agents and come out the other side with a working product.
The whole strategy rests on one idea: make every step small, verified, and reversible. You are not doing a big brave rewrite. You are doing a hundred boring safe moves, each one provably behavior-preserving, with a rollback point behind every one.
Step 1: Characterization tests FIRST — pin the behavior before you touch it
The cardinal sin of legacy refactoring is changing code whose behavior you can't reproduce. Legacy code has no spec; its spec is its current behavior, bugs included. Before you refactor anything, write characterization tests — tests that don't assert what the code should do, but capture what it currently does.
This is a perfect agent job. Point an agent at the module and tell it: "Write tests that pin the exact current behavior of this code, including edge cases and anything that looks like a bug. Do not fix anything. Just capture what happens." Then you run them and confirm they pass against the untouched code. Now you have a net. Any refactor that keeps these tests green has, by definition, preserved behavior — including the weird behavior something downstream might rely on.
- Agent writes characterization tests for the target module.
- You run them against the unchanged code. They must pass. If they don't, the tests are wrong — fix them until they pass against the original.
- Only now does refactoring begin. The tests are your definition of "still correct."
Step 2: Checkpoint before every autonomous run
Before you let an agent loose on a module, commit a checkpoint. Every time. This is the cheapest insurance in software:
git add -A && git commit -m "checkpoint: before extracting billing module"
When a run goes wrong — and on legacy code, some will — recovery is one line instead of an afternoon of untangling which of the agent's edits were good:
git diff # see what the agent actually did
git reset --hard # throw it all away, back to the checkpoint
The 30-second reset beats the 3-hour archaeology every single time. We go deeper on this and on isolating parallel agents in checkpoints and worktrees for AI coding. On legacy work the discipline is not optional; it is the thing that lets you be bold at all.
Step 3: Small, reversible steps with a green build between each
The failure mode of AI refactoring is the giant diff — the agent rewrites the whole module in one shot, forty files change, and you can't tell which change broke the one failing test. So don't allow the giant diff. Work in the smallest steps that make sense, and require a green build between every one.
- Give the agent one refactoring move: extract this function, rename this concept, invert this dependency. One move.
- Run the characterization tests. Green? The move preserved behavior. Commit it.
- Red? Reset to the checkpoint and either restate the move more narrowly or do it by hand. Do not let the agent "fix" a red test on legacy code — a green test it edited is a net you cut a hole in.
- Repeat. A hundred small safe moves get you to the same place as one big scary rewrite, except you can stop at any point with a working system.
The rule that keeps you safe: the build is green at every commit, or you don't commit. A legacy refactor that's half-done and green is a fine place to stop for the day. A legacy refactor that's half-done and red is a debugging session you didn't schedule.
Step 4: A reviewer agent from a different lab
Here's a rule we hold hard: the agent that reviews the refactor should come from a different lab than the agent that wrote it. Models from the same family share training lineage, and in practice they share blind spots — a Claude reviewing Claude-written code tends to wave through exactly the assumptions the builder made, because it would have made them too. On legacy code, those shared assumptions are precisely where the danger lives, because the whole point is that the code doesn't behave the way a modern model assumes it should.
So if your builder is a Claude model, run the review with GPT-5.3 Codex or Grok. If your builder is OpenAI, review with Opus or Fable. Ask the reviewer specifically: "Does this change alter any observable behavior? What could depend on the old behavior that we haven't tested?" A cross-lab reviewer has no loyalty to the builder's mistake. Our full process is in the AI code review workflow.
Step 5: Understand before you improve — reserve the flagship for comprehension
The hardest part of legacy work isn't writing new code; it's understanding code nobody wrote recently. This is the one place to spend on a top-tier model. Before any refactor, have a strong model — Fable 5 or Opus 4.8, with a large context window — read the module and explain what it does, what calls it, and what it assumes. Comprehension of unfamiliar code is exactly what the flagship models are best at, and getting the mental model right is worth far more than the token cost. Once you understand the code, cheaper builders can do the mechanical moves safely.
The order of operations, as a checklist
- Get it into git. No repo, no refactor. The safety net is git.
- Understand it. Flagship model reads the module and explains behavior, callers, and assumptions.
- Pin it. Agent writes characterization tests; you confirm they pass against the original.
- Checkpoint. Commit before every autonomous run.
- Move once. One small refactoring step at a time.
- Verify. Characterization tests green, or reset.
- Review cross-lab. A different family's model asks what behavior might have changed.
- Commit green. Only ever commit a green build. Squash the checkpoints before you merge.
The mindset
Refactoring legacy code with AI is not faster because the agent is smart. It's faster because the agent does the tedious parts — writing characterization tests, applying mechanical moves, spelling out what a gnarly function does — that a human would put off for months. Your job shifts from typing to verifying: keeping the steps small, the build green, and the rollback ready. Do that and you can refactor a system you're half-afraid of and sleep fine. A verification gate that runs your real build and tests after each autonomous run — the kind The Vibe Father provides — automates the green-or-reset loop, but a keen eye and plain git get you the whole way there.