Refactor the code below while preserving its externally observable behavior unless I explicitly approve a change. The code is [CODE]. It runs in [LANGUAGE AND RUNTIME], and its purpose is [CURRENT BEHAVIOR]. The constraints are [CONSTRAINTS]. Existing tests, callers, or error expectations are [TESTS AND COMPATIBILITY CONTEXT]. Start by summarizing the current control flow, data flow, public interfaces, side effects, and likely code smells. Identify behavior that is uncertain instead of guessing. Recommend the smallest refactor that materially improves [PRIMARY GOAL], such as readability, testability, separation of concerns, performance, or duplication reduction. Return: 1. A concise refactor plan, ordered so each step can be reviewed independently. 2. The complete revised code, not fragments, with file boundaries if more than one file is needed. 3. A behavior-preservation table mapping important old behavior to the new implementation. 4. Tests to add or update, covering happy paths, boundary values, null or empty inputs, malformed inputs, errors, async or concurrency behavior where relevant, and regression cases implied by the original code. 5. Any API, schema, performance, security, or deployment risk, plus a rollback approach. 6. A plain-English explanation of the design choices. Keep public method names, return shapes, exception behavior, logging, ordering, and mutation semantics intact unless I authorize a breaking change. Do not add dependencies or redesign unrelated modules. Do not invent types, services, tests, or requirements that are not supplied; label assumptions clearly. Ask up to 3 clarifying questions only if a required input is missing. Before answering, self-check that the revised code handles every original branch and that every proposed test can fail against the old bug or protect a stated behavior.
Fill in
| Placeholder | What to enter | Example |
|---|---|---|
| [CODE] | Paste the complete function, module, or related files you want to refactor. | async function loadOrders(id){ const u=await db.users.find(id); if(!u) throw Error('not found'); const x=await db.orders.where({userId:id}); return x.filter(o=>o.status!='cancelled').map(o=>({id:o.id,total:+o.total})) } |
| [LANGUAGE AND RUNTIME] | State the language, framework, version, and relevant execution environment. | TypeScript 5.4, Node.js 20, Jest |
| [CURRENT BEHAVIOR] | Describe what the code does today, including expected outputs and side effects. | Returns non-cancelled orders for a user and throws Error('not found') if the user does not exist |
| [CONSTRAINTS] | List requirements such as no new dependencies, latency limits, or style conventions. | No new packages; keep function signature and error message; database client is not typed |
| [TESTS AND COMPATIBILITY CONTEXT] | Paste relevant tests and describe callers, APIs, error contracts, or backward-compatibility needs. | Called by GET /users/:id/orders; tests cover missing user and one active order |
| [PRIMARY GOAL] | Name the main reason for the refactor, such as readability or easier testing. | Make database access and mapping easier to test |
How to use
- Paste the smallest complete unit that exposes the problem, plus its direct tests and callers.
- State which behavior is contractual, especially error messages, output ordering, and side effects.
- Run the suggested tests and compare outputs with representative production-like inputs before merging.
- Follow up with: "Now show this as a sequence of small commits, with the test that should accompany each commit."
Variations
Legacy module
Use this before changing a fragile module with sparse tests.
Assess this legacy [LANGUAGE] module: [CODE]. It is used by [CALLERS] and has [TEST COVERAGE]. Create a low-risk refactor plan before rewriting it. First list observable behavior, hidden coupling, shared mutable state, I/O boundaries, and areas where behavior cannot be inferred. Then provide characterization tests, followed by the smallest complete refactor that improves [GOAL]. Preserve interfaces and error behavior. Include a rollback point after each step and flag assumptions rather than inventing requirements. Ask up to 3 questions only if essential details are missing.
Performance refactor
Use this when correct code is too slow or memory-intensive.
Refactor [CODE] in [LANGUAGE AND RUNTIME] to improve [LATENCY OR MEMORY GOAL] without changing output or public APIs. Inputs have this size and distribution: [WORKLOAD]. Explain the present time and space complexity, identify the actual bottleneck, and distinguish algorithmic work from I/O. Return revised complete code, a benchmark plan, correctness tests, and edge cases involving empty, large, duplicate, and adversarial inputs. Do not claim a speedup without a measurement plan. Ask up to 3 questions only if required.
Extract service
Use this to separate business logic from a controller or UI component.
Extract the business logic from this [CONTROLLER OR COMPONENT]: [CODE]. The target boundary is [NEW SERVICE OR MODULE], and current dependencies are [DEPENDENCIES]. Propose a dependency direction that keeps HTTP, database, and UI concerns at the edges. Return file-by-file code, interfaces or types, updated tests, and a migration note for callers. Preserve validation, authorization, transactions, error mapping, and response shapes. Do not move code merely for aesthetics; explain each extracted responsibility. Ask up to 3 questions only when a missing detail changes the design.
Tips
- Write characterization tests before changing legacy code, especially for error paths and unusual input combinations.
- Treat output ordering, numeric coercion, logging, and mutation as behavior; callers often rely on them even when they are undocumented.
- Ask for complete files when imports, types, or helper functions affect correctness; isolated snippets can hide broken integration points.
- Measure performance before and after a performance refactor using a representative workload, not a single happy-path input.
FAQ
Can AI safely refactor code without tests?
It can help identify risks and write characterization tests, but absent tests make behavior preservation uncertain. Start by pinning down callers and representative inputs.
Should a refactor change the API?
Usually no. Separate a breaking API change from an internal refactor so review, rollout, and rollback remain manageable.
How do I prevent AI from over-engineering the result?
State the primary goal, forbid new dependencies unless justified, and ask for the smallest reviewable change.