Refactoring code with AI: small steps and tests

An agent can rewrite a module in a minute and break it just as fast. How to organize refactoring: tests first, one kind of change at a time, a commit per step, rollback.

5 min · · development · агенты

AI for code refactoring works well when you have set the frame: tests exist, the step is small and the result is checked by a command. Without that, an agent confidently rewrites a module, and an hour later it turns out behavior changed in three places nobody thought about. Below is a safe process: what to prepare first, how to word the steps, which rules to give the agent, how to roll back, and where AI really speeds things up and where it does not.

Step 0. Preparation: tests and a clean tree

Refactoring by definition does not change behavior, and "does not change" can only be proven with tests. Before you bring in an agent:

  1. Commit anything unrelated. The working tree should be clean so the agent's diff is visible on its own: git status is empty.
  2. Create a branch: git switch -c refactor/orders-service.
  3. Run the tests and confirm they pass before you start. Red tests before refactoring mean you cannot tell old breakage from new.
  4. Cover what you will change. If a module has no tests, the first task is writing "characterization" tests: they pin the current behavior, however odd it is. This task is convenient to hand to an agent: the result is checked by running it. There is a separate article on generating tests: auto-generating unit tests in Claude Code.
  5. Remember the check command, for example pytest -q or npm test. Give the same one to the agent.

Step 1. Rules for the agent

Agents read a project rules file (CLAUDE.md for Claude Code, AGENTS.md for Codex and opencode). For refactoring it helps to write a few lines there:

bash
## Refactoring rules
- Do not change behavior. Public signatures and response formats stay the same.
- One kind of change per step: renaming OR extracting a function OR typing.
- After each step run `pytest -q` and show the result.
- Do not touch files outside the named module without separate approval.
- Do not delete tests or weaken checks to make tests pass.
- If a step needs a design decision, stop and ask.

The last two points matter most. An agent told "make the tests pass" sometimes takes the shortest path: it edits the test, not the code. That is why the ban on editing tests is worth writing down explicitly.

Step 2. Small diffs

A big task like "refactor the orders service" yields a big diff that nobody can read. Split it into steps that can each be checked in a minute:

StepWordingWhat to check
1Add type annotations in orders/service.py without changing logictests green, diff only in signatures
2Extract the discount calculation from create_order into calc_discounttests green, logic moved verbatim
3Replace duplicated status checks with a call to ensure_statusthe list of replaced places matches grep
4Rename tmp and data2 meaningfullyonly names in the diff

After each step: read the diff, run the tests, make a commit such as refactor: add types to orders/service. Many small commits let you roll back exactly one step without losing the rest.

An example request to an agent for step 2:

bash
In orders/service.py extract the discount calculation from create_order into a
separate function calc_discount(order). Do not change or optimize the logic. Then
run pytest -q and show the result. If tests fail, do not fix the tests, explain what changed.

Step 3. Verification and rollback

After each step:

bash
git diff --stat          # how many files were touched
git diff                 # read it yourself
pytest -q                # verify behavior
git commit -am "refactor: extract calc_discount"

If something goes wrong:

  • git restore . discards uncommitted changes in the working tree;
  • git revert <hash> undoes a specific commit without rewriting history;
  • git reset --hard <hash> returns the branch to a known state but deletes everything after it, so use it deliberately.

You can automate verification with a hook: run the tests after every agent edit. How that is set up in Claude Code is described in the article on hooks.

AI for code refactoring: where it speeds things up and where it does not

It speeds up:

  • mechanical renames and moving code around the project;
  • extracting functions and classes, splitting long functions;
  • adding types, docstrings, annotations;
  • migrating to a new API call by a pattern (many repetitive replacements);
  • writing characterization tests before a change.

It does not help or gets in the way:

  • architecture decisions: where to put a responsibility, how to cut modules. The agent will suggest an option, but the choice is yours;
  • code without tests and without a way to run it locally: there is nothing to verify with;
  • dynamic spots (reflection, metaprogramming, string names), where finding references is unreliable and the agent may miss calls;
  • "optimizations" by eye: claims that the new version is faster need measurements.

A practical criterion: if the result of a step can be checked by a command in a minute, the step suits an agent. If checking requires a judgment about design, that is your work, and you give the agent the execution.

What a session costs

Suppose a refactoring takes 40 agent requests: on average 60,000 input tokens (history and files) and 1,200 output tokens per request. Input is 2.4M, output 0.048M. Zukko prices as of October 18, 2026, $ per 1M tokens.

Without cache:

ModelCalculationTotal
claude-sonnet-5-5 (0.4 / 2)2.4 × 0.4 + 0.048 × 2 = 0.96 + 0.096$1.06
gpt-6.1-sol (0.27 / 1.32)2.4 × 0.27 + 0.048 × 1.32 = 0.648 + 0.0634$0.71
deepseek-v4.1-flash (0.013 / 0.038)2.4 × 0.013 + 0.048 × 0.038 = 0.0312 + 0.0018$0.03

With Claude cache in the Anthropic format (assumption: of the 60,000 tokens in a request, 55,000 are read from cache and 5,000 new ones are written): read 40 × 55,000 = 2.2M × 0.05 = 0.11; write 40 × 5,000 = 0.2M × 0.5 = 0.10; output 0.096. Total about $0.31 instead of $1.06.

This is an illustration: the real cache share depends on the tool and session length. An important caveat: the Claude cache discount exists in the Anthropic format (Claude Code, /v1/messages), while through /v1/chat/completions all input is billed at the input price. The shorter the session and the fewer files the agent keeps in context, the smaller the input at every step. The strength index (claude-sonnet-5-5 has 48, deepseek-v4.1-flash has 39) does not guarantee the result: test two or three models on your own module.

FAQ

Can I trust AI with refactoring without tests?

You should not. Without tests you cannot tell preserved behavior from changed behavior. Characterization tests first, refactoring second.

Which AI for code refactoring should I choose?

We do not claim that one model does it better. Compare by price, context and strength index, then run the same small step through two or three models and compare the diff and the cost.

How do I stop the agent from rewriting too much?

Limit the task to one module and one kind of change, write the rules into CLAUDE.md or AGENTS.md, and read git diff --stat after every step.

What if the agent loops and spends money?

Set a daily limit on the key in the dashboard and watch usage. More in the article how not to lose money when an agent loops.

You can connect an agent to Zukko by following the Claude Code guide, and you create the key in the dashboard.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

AI for code refactoring: steps, tests, rollback · Zukko