Guide
How to Refactor
Refactor in small named steps, run tests after each one, never mix in behavior changes, and stop when the code is clean enough for the task at hand.
- Also known as
- Refactoring workflow
The loop
Make one small structural change, run the tests, commit, repeat. Every refactoring in this reference is built to fit that loop: each technique page has a ## Steps section where every step keeps the tests green.
- Confirm the tests pass before you start. A red suite means you cannot tell what you broke.
- Pick one smell and one technique. Do not start three at once.
- Apply the technique in the smallest steps that compile.
- Run the tests after each step.
- Commit when green, with a message that names the technique ("Extract Method: computeLateFee").
- Stop and reassess. The next smell may have disappeared or changed.
What a finished refactoring looks like
- The code is cleaner. If a reviewer cannot see the improvement, the change was not worth the diff.
- No new functionality. Behavior changes go in a separate commit, before or after.
- All tests pass. Not "most tests", and not "the ones I touched".
If any of these fails, you did something other than refactoring.
Before you start
Get a safety net
Tests that cover the code you will change are a precondition. If they do not exist:
- Write characterization tests: call the code with representative inputs, record what it returns today, and assert on that. You are pinning behavior, not judging it.
- For code that touches I/O, capture inputs and outputs at the boundary and replay them.
- Use the type checker as a second net. In TypeScript,
tsc --noEmitcatches broken call sites after a move or rename.
Read before you cut
Identify the smell by name. The smell page lists its treatments with "if X, then use Y" guidance. Picking the technique from the smell is more reliable than inventing a restructuring.
During the refactoring
- Prefer automated tools. IDE or language-server rename, extract and move are safer than manual edits because they update every reference.
- Change one thing per step. Rename, then move, then extract. Not all three in one edit.
- Keep the old path working while you build the new one. For example, when moving a method, leave a delegating stub in the old location, migrate callers, then delete the stub (Move Method, then Inline Method).
- Watch public interfaces. Renaming a private helper is free. Renaming an exported function affects callers you cannot see. Deprecate first, then remove.
- If the tests go red, revert. Do not debug a broken refactoring forward. Undo to the last green commit and take a smaller step.
Common sequences
| Goal | Sequence |
|---|---|
| Break up a long function | Extract Variable to name pieces, Extract Method per piece, Replace Temp with Query where temps block extraction |
| Split a god class | Extract Class, then Move Method and Move Field into it |
| Remove a type switch | Replace Type Code with Subclasses or Replace Type Code with State/Strategy, then Replace Conditional with Polymorphism |
| Shrink a parameter list | Introduce Parameter Object or Preserve Whole Object |
| Merge copies of logic | Extract Method, then Pull Up Method or Form Template Method if the copies live in sibling classes |
When to stop
Stop when the change you came to make is straightforward. Refactoring has no natural end; every codebase can be cleaner. The task defines the finish line, not your taste.
Signs you have gone too far:
- You introduced an interface with one implementation and no test double that needs it (Speculative Generality).
- Readers now jump through four files to follow what used to be one function.
- The diff touches files unrelated to the task.
Agent checklist
- The test suite was green before the first edit, and you recorded the command that runs it.
- Each step is one named technique, applied through tooling where available.
- Tests and type checks run after every step, not only at the end.
- A red result triggers a revert to the last green state, not a forward fix.
- Behavior changes are in separate commits from structural changes.
- You stopped when the target task became straightforward.
See also
- What Is RefactoringRefactoring changes the internal structure of code in small, behavior-preserving steps so it becomes cheaper to read and change.
- When to RefactorRefactor right before you change code, while fixing a bug and during review. Skip it when the code is due for deletion or has no tests.
- Technical DebtTechnical debt is the extra cost every future change pays because of shortcuts and drift in today's code. Learn its causes and how to pay it down.
- How Agents Should Use This ReferenceRules for AI coding agents using this reference: diagnose a named smell, read the exclusions first, apply one technique at a time and verify with tests.
- Extract MethodMove a coherent fragment of a long function into its own well-named function and call it from the original place.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.