Guide
When to Refactor
Refactor 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.
- Also known as
- Refactoring triggers
The short answer
Refactor the code you are about to change, right before you change it. That is when the cost of bad structure is real and the benefit lands immediately. Refactoring code that nobody will touch again is spending money on a building scheduled for demolition.
Good moments
Before adding a feature
Read the area you need to change. If the feature would be awkward to add, first reshape the code so the feature becomes a small, obvious addition. Make the change cheap first, then make the cheap change.
Typical sequence:
- The new payment method needs a fourth branch in a Switch Statements block that already has three.
- Apply Replace Conditional with Polymorphism or extract a Strategy map. Tests stay green.
- Add the new method as one new class or function.
When fixing a bug
A bug is evidence that the code is hard to understand. Before fixing it, restructure enough to make the bug visible: Extract Method to name the steps, Decompose Conditional to name the conditions. Often the bug becomes obvious. Then write a failing test, fix, and commit the refactoring and the fix separately.
The rule of three
The first time, write it. The second time you write something similar, note the duplication. The third time, extract the shared part. Two copies may diverge for good reasons; three copies are a pattern. See Duplicate Code.
During code review
Review is where a second reader finds names that mislead and functions that do too much. Small renames (Rename Method) and extractions are cheap to apply while the author still has context.
When you need to understand code
Renaming a variable once you understand what it holds stores that understanding in the code. The next reader, human or agent, does not need to rediscover it.
Bad moments
- The code works, is not changing and has no pending work. Leave it. Ugly but stable code has zero interest.
- The code is scheduled for deletion or replacement. Spend the effort on the replacement.
- There are no tests and you cannot add them cheaply. Refactoring without verification is guessing. Add characterization tests first, or restrict yourself to IDE-automated, provably safe transformations (rename, extract with a tool).
- You are close to a deadline and the change is not on the critical path. Record the debt (see Technical debt) and come back.
- The rewrite is honestly cheaper. If the code is so broken that it does not work, refactoring has nothing to preserve. Restructuring assumes correct behavior worth keeping.
- The task is not yours. An agent asked to fix one bug should not restructure three unrelated modules. Scope creep makes diffs unreviewable.
Decide in one pass
| Question | If yes | If no |
|---|---|---|
| Will you change this code in the current task? | Continue | Do not refactor |
| Do tests cover the behavior you will touch? | Continue | Add tests first, or stop |
| Would the task's diff be clearly smaller or safer after restructuring? | Refactor first | Make the change directly |
| Does the refactoring stay inside the files the task already touches? | Proceed | Note it, do not do it now |
Signals in the code
The smell catalog lists symptoms worth acting on. The most common triggers:
- A function longer than a screen: Long Method.
- A change that needs edits in many files: Shotgun Surgery.
- A method that uses another object's data more than its own: Feature Envy.
- The same group of three or four parameters passed everywhere: Data Clumps.
- Comments explaining what a block does: Comments.
Agent checklist
- The code you plan to refactor is in the path of the current task.
- Tests cover the behavior, or you added characterization tests first.
- You can state the concrete way the refactoring makes the task smaller or safer.
- The refactoring stays within the scope the user asked for; anything broader is reported, not done.
- Refactoring and behavior change land as separate commits or separate steps.
See also
- What Is RefactoringRefactoring changes the internal structure of code in small, behavior-preserving steps so it becomes cheaper to read and change.
- 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 to RefactorRefactor 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.
- Duplicate CodeThe same logic appears in two or more places, so every fix and change must be made several times and the copies drift apart.
- Long MethodA function that does several jobs in a row and has grown too long to read, name or test as one unit.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.