Refactors break when they’re too big, untestable, or quietly change behavior. These 18 prompts force smaller, verifiable steps the model can finish in one pass — and a human can review without re-reading the whole file.
What these prompts solve
A refactor prompt has one job: change shape without changing behavior. That means the prompt must (a) name the unit being changed, (b) state the invariants to preserve, (c) bound the scope so AI doesn’t drift into “while I’m here…” edits, and (d) force the model to show the plan or the diff before committing to it.
Who this is for
Senior engineers who use Claude Code, Cursor, or Codex to ship larger refactors than they could review by hand; tech leads consolidating legacy patterns; indie devs migrating a codebase between idioms (callback → async, class → hook, JS → TS); on-call engineers extracting a hotspot the night before a release.
When not to use these prompts
Skip them for one-line renames you can do with your editor — the prompt overhead is more than the work. Skip them when there are no tests at all — refactor without a safety net is rewriting, and these prompts assume a verification step exists. Don’t mix refactor with feature work in the same prompt; the model will silently sneak the feature into a “refactor” diff.
Prompt anatomy / structure formula
A refactor prompt should always carry six elements:
- Target unit: a specific function, file, module, or pattern — never “the codebase”.
- Goal shape: what the structure looks like after — “extract into module X”, “convert to async/await”, “split into ≤80-line functions”.
- Invariants: behavior preserved, public API preserved, test suite still green, no new dependencies.
- Scope fence: list of files the model may touch; everything else is read-only context.
- Plan-before-diff: tell the model to output the migration plan first, wait for confirmation, then apply.
- Verification hook: which tests to run, which lint rule must still pass, which type-check must still be clean.
Best for
- Module extraction and surface-area cleanup
- Strict-typing migration (TypeScript / mypy / Sorbet)
- Pattern modernization (callbacks → async, class → hooks)
- Splitting god-functions and god-files
- Eliminating one-off helpers in favor of stdlib
- Renames that ripple across many call sites
- Agent-mode refactor jobs (Claude Code / Cursor in agent mode)
18 copy-ready prompt templates
1. Extract a module
Use when a file has grown two responsibilities and one should move out.
You are a refactor agent. Extract {functionality} from {source-file} into a new module {target-path}. Constraints: preserve the public API of source-file, all existing tests must still pass, no behavior change. Output: (1) migration plan as a numbered list, (2) the new file's contents, (3) a minimal patch to source-file that re-exports the moved symbols. Do not change anything outside the listed files.
Variables: {functionality}, {source-file}, {target-path}.
Tip: Ask for the re-export shim explicitly so existing imports don’t break in the same PR.
2. Rename a symbol safely
Rename {old-name} → {new-name} across the repo. Steps: (1) list every usage with file:line, (2) show one representative call site before/after, (3) suggest semantic-preserving renames for any callers whose meaning is now wrong (e.g., variables named after the old name). Do not run find-and-replace; produce a per-file diff and stop for review.
Variables: {old-name}, {new-name}.
3. Inline an over-abstracted helper
Function {name} is used exactly once. Inline it at the call site. Output: the diff for both files, a one-sentence confirmation that behavior is identical, and the test command I should re-run.
4. Replace inheritance with composition
Below is a class hierarchy. Propose a composition-based refactor. Output: (a) new component/strategy interfaces, (b) per-class migration plan, (c) which tests need updates and why, (d) the order to apply the changes so the build is green at every step.
Hierarchy: {paste}
5. Modernize callbacks → async/await
Below is callback-based code. Convert to async/await. Preserve error semantics (rejected promises must carry the same error shape callbacks used to receive). Note any behavioral subtleties (e.g., parallelism that becomes sequential). Output the diff plus a "behavior delta" section listing every place semantics could differ.
Code: {paste}
6. Strict-typing migration
Migrate {file} from loose to strict typing ({source-config} → {target-config}). Pass 1: identify all implicit-any / unknown casts / missing return types. Pass 2: fix the most-confident ones first; leave a `// TODO type` for any case where the right type is unclear. Do not change runtime behavior.
File: {paste}
7. Split a god-function
Function {name} is 200+ lines. Propose a split into smaller functions. For each new function: name, single-sentence responsibility, signature. Do NOT change behavior. Output the diff in the order I should apply chunks so each commit is independently green.
Function: {paste}
8. Replace bespoke util with stdlib / library
Below is bespoke code. Identify any function reimplementing a stdlib or popular library utility. For each match: line range, suggested replacement (with import path), and the one edge case I should verify before deleting the bespoke version.
Code: {paste}
9. Boundary refactor — pull I/O to the edges
Function {name} mixes I/O (network / disk / DB) with pure logic. Refactor to pull I/O to the edges: extract a pure inner function that takes already-fetched inputs and returns a result; keep the outer function as the thin I/O shell. Output: new signatures, one-line on what's now testable that wasn't before.
10. Eliminate dead code
Audit {scope} for dead code: unexported symbols with no internal callers, exported symbols with zero downstream usage, feature flags whose value is fixed in code, branches that can never execute. For each: file:line, evidence it's dead, suggested removal. Skip anything reflective / dynamic (string-key access).
11. Hooks refactor (React) — extract custom hook
Component {name} mixes state, effects, and rendering. Extract the data layer into a custom hook `use{Name}` that returns `{ data, loading, error, refetch }`. Keep the component a pure render. Output the new hook file, the component diff, and one test idea per concern.
12. Database-query refactor — pull N+1 into a single fetch
Below is a code path that issues a query per loop iteration. Refactor into a single batched fetch. Constraints: result ordering preserved, error semantics preserved (one failure should still surface as one error), no new ORM features. Output: the diff plus a one-paragraph note on which DB index this now relies on.
Code: {paste}
13. Convert promise chain to async/await
Convert this promise chain to async/await. Preserve `.catch` semantics exactly — every rejection path must still be handled. If you remove a `.then` branch, justify it in one line. Output a side-by-side before/after.
Code: {paste}
14. Two-step deprecation refactor
Deprecate {api-name}. Step 1 (this PR): add the new API alongside the old one, mark the old with @deprecated, route 1 representative call site to the new API as a proof point. Step 2 (not this PR): migrate remaining call sites. Output: list of call sites with a recommended migration order — easiest first.
15. Cross-file constant consolidation
{constant} is duplicated in N files (paste below). Consolidate into one source of truth. Output: the canonical file/location, the diff for each consumer, and a check that the value matches in every original location before consolidation.
Files: {paste}
16. Agent-mode refactor brief (Claude Code / Cursor agent)
You have repo access. Refactor {pattern} → {new-pattern} across {scope}. Rules: (1) one file per commit, (2) tests must pass after each commit, (3) public API unchanged, (4) stop and ask if you encounter ambiguity — do not guess. Start by listing the files you will touch in commit order, then wait for my "go".
17. Behavior-preservation test gen
Below is the function I'm about to refactor. Before I touch it, write 6 characterization tests that lock in current behavior — including 2 tests for edge cases the existing tests miss. After the refactor I'll re-run them; they must still pass.
Function: {paste}
18. Refactor diff sanity check
Below is the diff I'm about to merge as "refactor only". Audit: any change that could alter behavior — control flow, error handling, return shape, exception type, null handling, ordering. For each suspicious change: file:line, what's potentially different, suggested test to confirm.
Diff: {paste}
Common mistakes
- Big-bang refactors with no verification step. The prompt didn’t ask for tests first; the model returned 400 lines of changes and you can’t tell what broke.
- Behavior changes snuck into a refactor. The model “fixed” a small bug while restructuring — now the diff mixes two intents and review takes 3× longer.
- Skipping the search-all-usages step on renames. You renamed in one file; a string-key reference somewhere else still uses the old name and silently breaks at runtime.
- Mixing refactor with feature work in one prompt. The model returns a refactor and a new option you never asked for. Always one intent per prompt.
- No scope fence. Without an explicit “do not touch X” list, the model drifts into adjacent files because it sees something it dislikes.
- No plan-before-diff step. You went straight to a patch; the plan was wrong but you only notice after applying it.
- Trusting the model’s “no behavior change” claim. Always run the characterization tests from template 17 — the model is wrong here surprisingly often.
How to push results further
- Pin one refactor pattern per prompt — extract, rename, inline. Mixing dilutes signal.
- For agent mode, demand one file per commit. Easier rollback, easier review, easier git bisect later.
- Run the diff sanity check (template 18) as a second pass on the model’s own output before merging.
- For migrations across many files, ask AI to first rank files by risk (most callers, most tests), then refactor low-risk files first to build confidence.
- Always pair a refactor prompt with a characterization test (template 17). Tests pinned before the change tell you what actually moved.
- Use the scope fence to keep the model honest: “files you may modify: A, B, C. Everything else is read-only.” Even agentic models respect this if it’s the first line of the prompt.
- After the refactor, ask the model to explain the diff back to you in 5 bullets. If it can’t summarize cleanly, the refactor is doing too much.
FAQ
- What’s the difference between a refactor prompt and a code-review prompt? Refactor changes the code; review evaluates it. Use code review prompts on the diff a refactor prompt produces.
- Why does the model keep adding features during a refactor? Constraint drift. Add a closing line: “If you find a bug or missing feature during this refactor, list it in a ‘NOTES’ section but do not change the code.” This isolates intents.
- How big a refactor can I do in one prompt? Roughly one function, one file, or one pattern across ≤5 files. Beyond that, split — the model loses track of invariants, and so do you when reviewing.
- Should I let agent mode auto-apply refactors? Only with one-file-per-commit and tests in CI. Without those, you lose your ability to bisect when something breaks two days later.
- What if AI says “no behavior change” but my tests fail? Trust the tests. Ask the model to diff its output against the original and find the line where semantics actually moved — it usually can.
- Refactor or rewrite? If >50% of the file changes, it’s a rewrite. Use migration plan prompts instead of refactor prompts for that.
Related
- Code review prompts — review the diff your refactor produces
- Claude Code execution prompts — agent-mode prompts that pair well with templates 16-18
- Migration plan prompts — when the change is too big to call a refactor
- Bug audit prompts — run before a refactor to know what behavior must be preserved
- Test generation prompts — generate the characterization tests in template 17
- API contract review prompts — verify your refactor didn’t change the public surface
- AI refactor workflow — end-to-end workflow that uses these prompts in sequence
Tags: #Prompt #AI coding #Refactor