Guide
How to Prompt an AI Agent for a Refactor
Prompt an AI agent for a refactor by naming the public behavior that must stay the same, the files in scope, the reason for the change and the checks that prove no drift. Avoid asking for a broad cleanup. Ask for one structural move at a time.
On this page
- What is the working pattern for AI refactor prompting?
- Can I adapt the prompt to my repo?
- How should a team run AI refactor prompting?
- What should you keep after the run?
- How do I refactor a large legacy codebase safely?
- Why is a refactor diff so hard to review?
- How big is too big for one refactor prompt?
- What do I do when the agent improves things I did not ask for?
What is the working pattern for AI refactor prompting?
The working pattern for AI refactor prompting is small enough to review and specific enough to repeat. Give the agent a named task and only the context it needs, then tie the result to a check you can rerun. The table below breaks that into moves.
- Move
- Start with a bounded task
- Use this when
- You have a named owner, target files and a clear done state
- Proof to save
- Issue, files, checks and owner are named
- Move
- Give the agent context
- Use this when
- The repo has patterns the agent must follow
- Proof to save
- Prompt cites files, errors and constraints
- Move
- Review the diff
- Use this when
- The task changes production code
- Proof to save
- Changed files, test output and risks are visible
| Move | Use this when | Proof to save |
|---|---|---|
| Start with a bounded task | You have a named owner, target files and a clear done state | Issue, files, checks and owner are named |
| Give the agent context | The repo has patterns the agent must follow | Prompt cites files, errors and constraints |
| Review the diff | The task changes production code | Changed files, test output and risks are visible |
A good AI coding workflow is specific enough to review and small enough to recover.
Each of those moves fails in its own particular way, and the order they come in is doing more work than it looks like it is. Skip the boundary and you get a diff nobody wants to read, because the agent has quietly rewritten files you never meant to open. Thin context fails more quietly: the code compiles, the tests pass, and it ignores every pattern the rest of the repo follows. Then there's the check, which to me is the real one, because it's the whole difference between a result you verified and a result you're taking someone's word for.
The step people skip is the plan. I think it's because it feels like overhead, thirty seconds of nothing visibly happening while you're trying to get work done. Actually, that's not quite the reason. It's that the cost of skipping it lands much later, so it never registers as the mistake it was. If the plan names files you didn't expect, you've learned something for free. If it names the right ones, you've got a reference to check the diff against when it arrives. And once there are four hundred lines on the screen, changing the approach means throwing that work away, which nobody is good at.
This is covered hands-on in Agent Mode Foundations — 6 short modules, free to read.
Can I adapt the prompt to my repo?
Yes. The frame below is a starting point, not a script. Fill in your own files, constraints and done state, and keep the plan step so the agent commits to an approach before it writes code.
Task: [one outcome] Context: [files, errors, docs and examples] Boundary: [what not to touch] Done when: [test, typecheck, screenshot or review proof] Before editing, write a short plan with files, risk and checks.
The Boundary line is the one people leave out, and probably the one doing the most work. Without it every file the agent can reach is fair game, so you end up reviewing incidental edits to config and imports and some helper you'd forgotten existed, all mixed in with the change you actually wanted. Naming what not to touch takes a few seconds. Reverting it afterwards does not.
"Done when" has to name something you can run. A test name, a typecheck, a command whose output you can read, a screenshot of one specific state. "Done when the bug is fixed" doesn't count, and I'd say that's the single most common version of this mistake, because it quietly hands the judgment back to the agent, and the agent is going to tell you it's finished either way.
How should a team run AI refactor prompting?
Running AI refactor prompting as a team comes down to one habit: leave a trail the next reviewer can follow. The steps below keep the prompt and its proof attached to the change, so nobody has to reverse-engineer what the agent did.
- 1Pick one real backlog item with a clear owner and expected result.
- 2Add only the context the agent needs: files, failing output, constraints and done state.
- 3Ask for a plan before code when the task touches more than one file.
- 4Run checks that match the risk: unit test, typecheck, visual pass or review checklist.
- 5Capture the prompt, diff, result and reviewer note so the workflow can be repeated.
Task, context, constraints, done state and checks.
Open the diff, read changed files and rerun the check yourself.
Prompt, diff, test output and the review note that proved the result.
What should you keep after the run?
Keep whatever lets you rerun the work or hand it to someone else. A finished task is the merged code plus the short trail that explains how it got there.
- The prompt or plan that shaped the work.
- The files changed and the reason each file changed.
- The command, screenshot or review note that proved the result.
- The rule, checklist or template you would reuse next time.
How do I refactor a large legacy codebase safely?
Pin the behavior before you touch it. For a big, idiosyncratic repo, treat tests as guardrails: identify the current behavior, write tests that lock that current state, then make the minimal change while keeping inputs and outputs identical. This is exactly how you handle a language migration, say legacy Java to something faster like Go - same inputs, same outputs, better speed and flexibility.
- 1Identify the current behavior you must not break.
- 2Write characterization tests that pin that current state.
- 3Make the smallest code change that meets the new goal.
- 4Keep input and output identical, and rerun the pinned tests.
The tests aren't there to prove the new design is clever. They are there to prove you changed nothing the caller can see.
identify the current state, write tests for that current state, and then do the minimal code changes... by maintaining the output of the current state.
This scales to genuinely large repos. A demo ran against Grafana: roughly 70K stars, about ten years old, 13K-plus contributors, potentially gigabytes of files. Cursor's semantic indexing handles new-and-small through old-and-huge the same way, via semantic search plus efficient grep inside the agent harness.
Why is a refactor diff so hard to review?
Because everything changed and nothing did. A reviewer's usual method is to read the diff and ask what each line changes about the program's behaviour, and in a clean refactor the answer is nothing, on every line. There is no signal left in the diff to read. That is the real reason to pin behaviour in tests before you start. The tests do the reviewing; the diff only shows that something moved.
Tests written afterwards cannot help with this. They prove the new code agrees with itself, and the behaviour they would have needed to capture is already gone.
A test you have never seen fail proves less than it appears to, as well. There are reports of generated tests that log a failure with something like reporter.log instead of calling the framework's fail() method, or wrap the assertion in a try-catch that swallows it, so make each guardrail go red for the reason you expect before you let it stand as one.
How big is too big for one refactor prompt?
Size is the wrong axis, and I spent a while getting this wrong. The advice I used to give was keep the diff small. What actually predicts a bad refactor run is how many judgment calls sit inside it. A rename across sixty files is enormous and completely checkable, one grep at a time. A forty-line change to how a module gets its dependencies is tiny and can take an afternoon to be sure about.
So ask for one structural move per prompt, and choose the move by whether you could describe it to someone in a single sentence. Extract this into its own module. Rename this concept everywhere it appears. If the description runs to a paragraph, you are looking at two moves sharing one name.
What do I do when the agent improves things I did not ask for?
Send the run back rather than review it. A coding agent has a bias for action and will happily rewrite a module, so a mechanical move comes back carrying a rename you never asked for and a tidied import block. Now you cannot tell the move from the change, and the pinned tests will not flag it, because nothing in them asserts anything about the behaviour that just got added.
The Boundary line in the frame above is where this gets prevented, and on a refactor I would spend more words there than on the task itself. Name the files in scope, then name the things that are not part of this move.
Probably worth adding that improvements are welcome as a separate follow-up. The suggestions are often fine, just not in this diff.
Frequently asked questions
Who is this guide for?
Developers using AI agents for refactors in production repos.
What should I do next?
Start with one real repo task, capture the prompt and review the result before scaling the workflow.
Sources & last verified
- Cursor docs: prompting agents
- Cursor Learn: context
- Cursor Learn: working with agents
- Cursor agent best practices
Cursor ships frequently. Facts verified against primary sources on July 9, 2026.
Keep reading
Rather do it than read about it? Run 11 interactive Cursor walkthroughs in a simulated editor. Free, no account needed.