Refactoring Navigator
Refactoring is changing the shape of code without changing what it does — and the second half of that sentence is the hard half. This skill plans and executes refactors as a sequence of small, independently safe moves, each verified before the next begins, so the work can stop at any point and still leave the codebase better than it started. Big-bang rewrites are a last resort with a named justification, never a default.
When to use this skill
- Code needs restructuring before a feature can land sanely ("make the change easy, then make it")
- Duplication, a god function, or a tangled module is slowing every change in the area
- A rename, extraction, or move ripples across many files and needs sequencing
- A prototype survived contact with users and must now become maintainable
Workflow
- Name the target shape in one sentence — "callers construct invoices only through the factory", "no module outside storage/ touches the connection pool". A refactor without a testable end condition wanders forever.
- Establish a behavior net before moving anything. Find the tests that pin current behavior. If the code is untested, write characterization tests first: feed it representative inputs and assert whatever it does today — even behavior that looks wrong. Wrong-but-depended-on is still behavior; note it for a separate fix later.
- Decompose into moves, each satisfying three rules: the build stays green, behavior is unchanged, and the move is reversible on its own. The classic sequence is introduce the new structure alongside the old, migrate callers one cluster at a time, then delete the old path — and migration and deletion are always separate steps.
- Order moves by information gained. Do the move most likely to reveal a hidden coupling first, while the sunk cost is one step rather than nine.
- Execute one move, verify, commit. Run the behavior net after every move. One move per commit, subject line stating the move ("Extract rate lookup from Invoice.total"), so review and revert operate at move granularity.
- Keep a strict no-improvements ledger. Every bug, missing test, and design smell noticed mid-refactor goes into the ledger, not into the diff. Mixing fixes into a refactor destroys the reviewer's ability to check "behavior unchanged" — the ledger becomes follow-up work.
- Stop at the target shape. When the end condition from step 1 holds, stop. Report the ledger and let the user choose what comes next; momentum is not a mandate.
Output format
Present the plan before executing it:
Target shape: <one sentence>
Behavior net: <existing tests kept green + characterization tests added>
Moves:
1. <move> — files touched — risk: <what could reveal itself>
2. <move> — ...
Stop condition: <how we know we are done>
Ledger (found, not fixed): <grows during execution>
During execution, report after each move: move completed, net status, ledger additions.
Guardrails
- Never refactor and change behavior in the same commit. If a behavior change turns out to be required, stop and surface it — that is a decision for the user, not a step in the plan.
- A move that cannot be verified (no tests reach it, no way to exercise it) is a move you have not earned yet; build the net first.
- If two consecutive moves each uncovered a surprise coupling, pause and re-plan; surprises arriving in pairs mean the map is wrong.
- Renames ride alone. A commit that renames and edits logic hides the edit inside a wall of mechanical diff.
- Resist scope creep from the ledger. The ledger is the pressure valve that keeps the refactor pure — the moment its items leak into the diff, the refactor stops being reviewable.