// guide

How to Prompt AI for Refactoring

To refactor safely with AI, first make sure tests pin down the current behavior, then ask for a plan, approve one small step at a time, state what must not change, and run the tests after every step. Refactoring means changing structure without changing behavior, so your prompt should say exactly that.

By Published Last updated 3 min read

Why refactoring needs careful prompting

AI assistants are good at mechanical restructuring and bad at knowing which behavior is intentional. Asked to “clean up” a module, a model will happily fix “bugs” your customers rely on, change error messages other systems parse, or reorder operations with side effects. The fix is to make “don’t change behavior” concrete and checkable.

Step 1: Pin down current behavior with tests

Before any refactoring, make sure tests describe what the code does today, including its odd parts. If coverage is thin, start with this prompt:

text
Read src/billing/invoice.ts. Don't change it.
Write characterization tests that capture its current behavior,
including edge cases: zero amounts, negative amounts, missing tax
rates, rounding. If a behavior looks like a bug, still test the
current behavior and add a comment "CURRENT BEHAVIOR, possibly a bug".

Run the new tests against the unchanged code. They must all pass before you continue.

Step 2: Ask for a plan, not code

Have the assistant propose the steps and wait for your approval. Planning first catches bad ideas while they are cheap to reject.

text
I want to refactor src/billing/invoice.ts so tax calculation is a
separate, pure function that can be tested on its own.

Constraints:
- No behavior changes. The characterization tests must keep passing
  unchanged.
- Don't change the public exports of invoice.ts.
- No new dependencies.

Propose a plan as a numbered list of small steps, each one leaving
the tests passing. Don't write code yet.

Reject steps that mix restructuring with behavior changes, and steps too large to review in one go.

Step 3: One step at a time

Approve and run one step, verify it, commit it, then continue.

text
Do step 1 only. Then run the tests and show me the result.

Small steps keep each diff readable, and a commit per step gives you an easy way back if a later step goes wrong.

Step 4: State what must not change

Name the things that are easy to break by accident. Good constraints to include:

  • public function names, signatures and exports
  • error types and error message text
  • log messages and metrics names other systems depend on
  • the order of side effects, such as database writes and emails
  • performance-sensitive paths (“don’t add database queries inside loops”)

Step 5: Verify every step

After each step, check the diff and the test results yourself. Watch for edited test files: in a pure refactoring, existing tests should not need to change. If the assistant changed a test to make it pass, stop and ask why.

Prompt patterns for common refactorings

Goal Prompt pattern
Extract a function “Extract lines 40–85 of report.py into a function named build_summary with explicit parameters. No behavior change. Show the diff.”
Rename across the codebase “Rename CustomerDTO to CustomerRecord everywhere, including tests and docs. List any string references you found but didn’t change.”
Remove duplication “These three handlers duplicate validation logic. Propose one shared helper. Show how each handler would call it before changing anything.”
Replace a library “Replace moment with date-fns in src/reports/ only. Keep output strings identical; add tests for the formats before switching.”
Simplify a long function “Explain what process_order does in numbered steps. Then propose how to split it into functions of under 30 lines without changing behavior.”
Untangle conditionals “Rewrite the nested conditionals in pricing.ts as early returns. Keep every branch’s result identical; add a test for each branch first.”

When not to use AI for a refactoring

Do it by hand, or in much smaller steps, when the code has no tests and can’t easily get them, when behavior depends on timing or concurrency, or when you can’t verify the result. An AI that produces a large, untested refactoring quickly hasn’t saved you time; it has moved the work into review and production.

Sources

  1. Martin Fowler, refactoring.com
  2. Martin Fowler, "Definition Of Refactoring"
  3. Anthropic, "Building effective agents" (2024)
  4. Wei et al., "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models" (2022)