Expert code refactoring based on Martin Fowler's catalog — improve maintainability without changing behavior. Covers code smells, composing methods, moving features, organizing data, simplifying conditionals, method calls, and generalization. Triggers on: refactor, 重构, clean up, improve code, code smell, extract method, rename, simplify.
---
name: refactor
description: "Expert code refactoring based on Martin Fowler's catalog — improve maintainability without changing behavior. Covers code smells, composing methods, moving features, organizing data, simplifying conditionals, method calls, and generalization. Triggers on: refactor, 重构, clean up, improve code, code smell, extract method, rename, simplify."
user-invocable: true
---
# Refactor — Expert Code Restructuring
Surgical code refactoring based on Martin Fowler's <Refactoring> (2nd Edition) catalog. Improve structure, readability, and maintainability without changing external behavior. Gradual evolution, not revolution.
---
## When to Use
This skill activates when:
- Code is hard to understand or maintain
- Functions/classes have grown too large
- Code smells are detected
- Adding features is difficult due to poor structure
- User explicitly requests refactoring, cleanup, or improvement
- User says: refactor, 重构, clean up, improve code, code smell, extract method, rename, simplify
---
## The Golden Rules
These five rules are non-negotiable. Violating any of them turns refactoring into reckless editing.
### 1. Behavior is Preserved
Only *how* the code works changes, never *what* it does. If tests existed before, they must pass after. If the refactoring introduces a behavioral change, it's not refactoring — it's rewriting.
### 2. Small Steps
Each change should be the smallest possible transformation that compiles and passes tests. If a step breaks, you know exactly which change caused it. Refactoring is a series of tiny, safe transformations, not one big rewrite.
### 3. Version Control is Your Friend
Commit before starting. Commit after each successful step. This gives you infinite undo. Branch from a clean state so you can abandon the refactoring without consequences.
### 4. Tests are Essential
"Without tests, you're not refactoring — you're just editing." If tests don't exist for the target code, write characterization tests first. These tests capture the current behavior so you can detect regressions.
### 5. One Thing at a Time
Never mix refactoring with feature changes. Never refactor two unrelated things simultaneously. Each commit should contain exactly one refactoring operation.
---
## When NOT to Refactor
| Scenario | Action |
|----------|--------|
| Code works and won't change again | Leave it alone |
| Critical production path with no tests | Write characterization tests first |
| Under tight deadline pressure | Document the smell, refactor later |
| No clear purpose or benefit | Don't refactor for refactoring's sake |
| Code is fundamentally wrong | This is a rewrite, not a refactoring |
---
## Code Smells Catalog
Based on Fowler's taxonomy. Before refactoring, identify which smell is present.
### Bloaters
| Smell | Description | Primary Refactoring |
|-------|-------------|-------------------|
| **Long Method** | Method > 10-15 lines, doing multiple things | Extract Method, Replace Temp with Query |
| **Large Class** | Class with too many fields/methods (God Object) | Extract Class, Extract Subclass |
| **Primitive Obsession** | Using primitives instead of small objects | Replace Data Value with Object, Replace Type Code with Class |
| **Long Parameter List** | Method with > 3-4 parameters | Introduce Parameter Object, Preserve Whole Object |
| **Data Clumps** | Same group of data appearing together | Extract Class, Introduce Parameter Object |
### Object-Orientation Abusers
| Smell | Description | Primary Refactoring |
|-------|-------------|-------------------|
| **Switch Statements** | Repeated switch/if-else on type codes | Replace Conditional with Polymorphism, Replace Type Code with Subclasses |
| **Temporary Field** | Field only set in certain circumstances | Extract Class, Introduce Null Object |
| **Refused Bequest** | Subclass doesn't use inherited members | Replace Inheritance with Delegation, Push Down Method/Field |
| **Alternative Classes with Different Interfaces** | Classes doing similar things with different names | Rename Method, Move Method, Extract Superclass |
### Change Preventers
| Smell | Description | Primary Refactoring |
|-------|-------------|-------------------|
| **Divergent Change** | One class changed for different reasons | Extract Class |
| **Shotgun Surgery** | One change requires many small changes across classes | Move Method, Move Field, Inline Class |
| **Parallel Inheritance Hierarchies** | Adding a subclass to one hierarchy forces adding to another | Move Method, Move Field |
### Dispensables
| Smell | Description | Primary Refactoring |
|-------|-------------|-------------------|
| **Comments** | Comments explaining what code does (not why) | Extract Method, Rename Variable, Introduce Assertion |
| **Duplicate Code** | Same code structure in multiple places | Extract Method, Pull Up Method, Form Template Method |
| **Lazy Class** | Class doing too little to justify existence | Inline Class, Collapse Hierarchy |
| **Data Class** | Class with only fields and getters/setters | Move Method, Encapsulate Field, Encapsulate Collection |
| **Dead Code** | Unused code, imports, commented-out blocks | Delete it (git history has it) |
| **Speculative Generality** | Code built for "someday" that never came | Inline Class, Collapse Hierarchy, Remove Parameter |
### Couplers
| Smell | Description | Primary Refactoring |
|-------|-------------|-------------------|
| **Feature Envy** | Method uses another class's data more than its own | Move Method, Extract Method + Move Method |
| **Inappropriate Intimacy** | Classes know too much about each other's internals | Move Method, Move Field, Replace Delegation with Hidden Delegate |
| **Message Chains** | `a.getB().getC().getD().doSomething()` | Hide Delegate, Extract Method |
| **Middle Man** | Class delegates everything to another class | Remove Middle Man, Inline Method |
| **Incomplete Library Class** | Library missing methods you need | Introduce Foreign Method, Introduce Local Extension |
---
## Refactoring Techniques Catalog
Organized by category, from Fowler's catalog. Each technique includes its mechanical steps.
### Composing Methods
#### Extract Method
Turn a code fragment into a method whose name explains its purpose.
**Mechanics:**
1. Create a new method named after what the fragment does (not how)
2. Copy the extracted code into the new method
3. Identify local variables: read-only become parameters, modified become return values
4. Pass parameters and handle return values
5. Replace the original fragment with a call to the new method
6. Test
**Before:**
```java
void printOwing() {
printBanner();
// Print details
System.out.println("name: " + _name);
System.out.println("amount: " + getOutstanding());
}
```
**After:**
```java
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + _name);
System.out.println("amount: " + outstanding);
}
```
#### Inline Method
Replace a method call with its body when the method body is as clear as the name.
**Mechanics:**
1. Check the method is not polymorphic (no subclasses override it)
2. Find all callers
3. Replace each call with the method body
4. Delete the method definition
5. Test
#### Extract Variable
Put the result of an expression (or part of it) in a self-explanatory variable.
**Before:**
```java
if (platform.toUpperCase().indexOf("MAC") > -1 &&
browser.toUpperCase().indexOf("IE") > -1 &&
wasInitialized() && resize > 0) {
// ...
}
```
**After:**
```java
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
// ...
}
```
#### Inline Temp
Replace a temp variable with its expression when the temp is only used once and the expression is clear.
#### Replace Temp with Query
Extract the expression into a method. Temps that are computed once and reused are replaced with method calls.
#### Split Temporary Variable
A temp assigned more than once (not loop/collecting) should be split into separate variables, one per responsibility.
#### Remove Assignments to Parameters
Don't assign to parameters. Use a local variable instead.
#### Replace Method with Method Object
When a long method uses many local variables that make Extract Method hard, turn the method into its own class, with locals as fields.
#### Substitute Algorithm
Replace an algorithm with a clearer one.
---
### Moving Features Between Objects
#### Move Method
Move a method to the class where it's used most.
**Mechanics:**
1. Check all features used by the method on its current class
2. Check for polymorphism (subclass/superclass methods)
3. Create the method on the target class, adapting as needed
4. Reference the target object from the source
5. Turn the source method into a delegating method, or remove it
6. Test
#### Move Field
Move a field to the class where it's used most.
#### Extract Class
When a class does the work of two, split it. Create a new class and move relevant fields and methods.
#### Inline Class
When a class does almost nothing, absorb it into the class that uses it most.
#### Hide Delegate
Create methods on the server to hide the delegate chain. `manager = person.getDepartment().getManager()` → `manager = person.getManager()`.
#### Remove Middle Man
When a class is doing too much delegation, call the delegate directly.
#### Introduce Foreign Method
When a server class needs an additional method but you can't modify it, create a method on the client with the server instance as the first argument.
#### Introduce Local Extension
When you need multiple foreign methods, create an extension class (subclass or wrapper).
---
### Organizing Data
#### Self Encapsulate Field
Access fields through getters and setters, even within the owning class.
#### Replace Data Value with Object
When a data item needs additional data or behavior, turn it into an object.
**Before:**
```java
class Order {
private String customer; // Just a string
}
```
**After:**
```java
class Order {
private Customer customer; // Rich object with name, address, credit rating
}
```
#### Change Value to Reference
When you need to share one instance of an object across multiple places.
#### Change Reference to Value
When a reference object is small, immutable, and you want value semantics.
#### Replace Array with Object
When an array holds heterogeneous data (`String[] row = new String[3]` — name, score, wins), replace with an object.
#### Duplicate Observed Data
Domain data lives in a GUI control but domain logic needs it. Copy the data into a domain object and set up an observer to keep the two in sync (Observer pattern). Separates presentation from domain so each can evolve independently.
#### Change Unidirectional Association to Bidirectional
Two classes need each other's features but only one holds a reference. Add a back-pointer and make the modifiers on both ends keep the link consistent. Add the reference only when genuinely needed — bidirectional links raise coupling and risk inconsistency.
#### Change Bidirectional Association to Unidirectional
A two-way link exists but one side no longer uses the other. Drop the unneeded direction. Reduces coupling, simplifies lifecycle management, and avoids "zombie" objects kept alive only by a stale back-pointer.
#### Replace Magic Number with Symbolic Constant
Replace literal numbers/strings with named constants.
#### Encapsulate Field
Make public fields private and provide accessors.
#### Encapsulate Collection
Never return the raw collection. Return a read-only view and provide add/remove methods.
#### Replace Type Code with Class
Replace a numeric/string type code with a class that has meaningful behavior.
#### Replace Type Code with Subclasses
When type code affects behavior, use polymorphism instead of conditionals.
#### Replace Type Code with State/Strategy
Similar to subclasses but uses composition when the type can change at runtime.
#### Replace Subclass with Fields
When subclasses vary only in constant data, replace them with fields on a single class.
---
### Simplifying Conditional Expressions
#### Decompose Conditional
Extract the condition, then-part, and else-part into separate methods.
**Before:**
```java
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * _winterRate + _winterServiceCharge;
} else {
charge = quantity * _summerRate;
}
```
**After:**
```java
if (isSummer(date)) {
charge = summerCharge(quantity);
} else {
charge = winterCharge(quantity);
}
```
#### Consolidate Conditional Expression
Combine multiple conditionals that have the same result.
#### Consolidate Duplicate Conditional Fragments
Move code that appears in every branch outside the conditional.
#### Remove Control Flag
Replace control flags with break, continue, or return.
#### Replace Nested Conditional with Guard Clauses
Use early returns for special cases instead of deep nesting.
**Before (arrow code):**
```java
double getPayAmount() {
double result;
if (_isDead) {
result = deadAmount();
} else {
if (_isSeparated) {
result = separatedAmount();
} else {
if (_isRetired) {
result = retiredAmount();
} else {
result = normalPayAmount();
}
}
}
return result;
}
```
**After:**
```java
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
}
```
#### Replace Conditional with Polymorphism
When a conditional chooses different behavior based on the type of an object, use subclasses.
#### Introduce Null Object
Replace null checks with a null object that provides default behavior.
#### Introduce Assertion
State assumptions explicitly with assertions.
---
### Making Method Calls Simpler
#### Rename Method
The name should say what the method does. If you can't think of a good name, the method may have multiple responsibilities.
#### Add Parameter / Remove Parameter
Add parameters when a method needs more info. Remove parameters when the method can get the info another way.
#### Separate Query from Modifier
A method should either return a value OR change state, never both.
#### Parameterize Method
Several methods doing similar things with different values → one method with a parameter.
#### Replace Parameter with Explicit Methods
The inverse: when a parameter essentially selects different behavior, create separate methods.
#### Preserve Whole Object
Pass the whole object instead of pulling individual fields from it.
#### Replace Parameter with Method
*(refactoring.guru: Replace Parameter with Method Call)* When a parameter can be computed from data the object already has, remove the parameter and let the method call the query itself.
#### Introduce Parameter Object
Group parameters that naturally go together into an object.
#### Remove Setting Method
Make a field immutable by removing its setter and setting it in the constructor.
#### Hide Method
Make methods private when they're not used outside the class.
#### Replace Constructor with Factory Method
When you need more flexibility than a simple constructor call.
#### Replace Error Code with Exception
Throw an exception instead of returning an error code.
#### Replace Exception with Test
Check the condition first instead of catching an exception.
---
### Dealing with Generalization
#### Pull Up Field/Method/Constructor Body
Move identical fields/methods/constructor code from subclasses to superclass.
#### Push Down Method/Field
Move behavior from superclass to only the subclasses that use it.
#### Extract Subclass
Create a subclass for a subset of features used in some instances.
#### Extract Superclass
Create a superclass for shared features of similar classes.
#### Extract Interface
Create an interface from a subset of a class's public methods.
#### Collapse Hierarchy
Merge a superclass and subclass when they're not different enough.
#### Form Template Method
Generalize an algorithm in the superclass, letting subclasses fill in the specifics.
#### Replace Inheritance with Delegation
When a subclass only uses part of the superclass, use composition instead.
#### Replace Delegation with Inheritance
When a delegating class needs access to all of the delegate's behavior.
---
## The Refactoring Process
### Phase 1: Prepare
1. **Write characterization tests** if they don't exist. These capture current behavior — they don't need to be elegant, just comprehensive enough to catch regressions.
2. **Commit** current state. Start from a clean working tree.
3. **Create a branch** for the refactoring. Keep it separate from feature work.
### Phase 2: Identify
1. **Smell the code.** Use the smell catalog above to classify what's wrong.
2. **Understand the code.** Read it thoroughly. You must understand what it does before changing it.
3. **Choose the right refactoring.** Pick from the technique catalog. Know what the result looks like before you start.
### Phase 3: Refactor (Small Steps)
For each step:
1. **Make one small change.** One refactoring technique at a time.
2. **Compile.** The code should compile after every change.
3. **Run tests.** All tests must pass. If they don't, you've changed behavior.
4. **Commit.** Create a commit with a message like `refactor: extract validateEmail method`.
Repeat until the smell is resolved.
### Phase 4: Verify
1. **All tests pass.** Non-negotiable.
2. **Manual check.** Briefly run the application or review the diff for unintended changes.
3. **Performance.** Ensure no performance regression. Simple refactorings rarely cause them, but check.
### Phase 5: Clean Up
1. **Remove stale comments.** If a refactoring made a comment obvious, delete the comment.
2. **Check for dead code.** After refactorings, unused code may emerge.
3. **Final commit.** Summarize the refactoring sequence.
---
## Refactoring Checklist
### Code Quality
- [ ] Functions are small (< 20 lines preferred, < 50 lines max)
- [ ] Each function does one thing (single responsibility)
- [ ] No duplicated code (DRY)
- [ ] Names describe what, not how
- [ ] No magic numbers or strings
- [ ] Dead code removed
### Structure
- [ ] Related code is grouped together
- [ ] Module boundaries are clear
- [ ] Dependencies flow in one direction (no cycles)
- [ ] No circular dependencies
### Conditionals
- [ ] Guard clauses replace deep nesting
- [ ] Complex conditions extracted to named methods
- [ ] Polymorphism replaces type-switching conditionals
- [ ] Null Object pattern where appropriate
### Type Safety (typed languages)
- [ ] Types defined for all public APIs
- [ ] No `any` usage without qualification
- [ ] Nullable types explicitly marked
- [ ] Type codes replaced with classes/enums
### Testing
- [ ] Refactored code is tested
- [ ] Edge cases are covered
- [ ] All tests pass after each step
- [ ] Characterization tests capture pre-refactoring behavior
---
## Language-Specific Guidance
### Java
- Prefer `final` for locals that shouldn't change
- Use IDE automated refactorings (Eclipse/IntelliJ) for mechanical steps
- Leverage the type system: enums, records (Java 14+), sealed classes (Java 17+)
### JavaScript/TypeScript
- Use destructuring to reduce parameter count
- Prefer `const` over `let` for immutable bindings
- Use TypeScript union types instead of type codes
- Nullish coalescing (`??`) and optional chaining (`?.`) eliminate null-check noise
### Python
- Use type hints for documenting intent during refactoring
- Use `dataclasses` to replace tuple/data-class patterns
- Use `@property` to replace getters
- Context managers for resource cleanup patterns
### Go
- Small interfaces preferred: accept interfaces, return structs
- Use named return values when they improve clarity
- Table-driven tests pair well with refactoring
- Avoid deep nesting with early returns
### Rust
- Use `Result` and `Option` instead of error codes and null
- Pattern matching replaces if-else chains
- `From` trait implementations clean up type conversions
- Derive macros reduce boilerplate
---
## Common Refactoring Sequences
### Extract Method Sequence
1. Create a new method named after intent
2. Copy code fragment into new method
3. Identify local variables → parameters / return values
4. Call new method from original location
5. Test
### Replace Conditional with Polymorphism Sequence
1. Create subclasses for each variant
2. Create a factory method that returns the right subclass
3. Move the conditional body to the appropriate subclass method
4. Delete the conditional
### Extract Class Sequence
1. Identify a coherent subset of fields and methods
2. Create a new class
3. Create an instance from the old class
4. Move fields and methods one at a time
5. Update references in old class
6. Test after each move
### Inline Class Sequence
1. Identify all callers of the target class
2. Move all methods/fields to the absorbing class
3. Redirect all references to the absorbing class
4. Delete the empty class
5. Test
---
## Safety Protocol
### Before You Touch Anything
```
1. Characterization tests → capture what the code does now
2. Git commit → save a known-good state
3. Branch → isolate refactoring from other work
```
### Every Single Step
```
1. One change → one refactoring technique
2. Compile → must compile clean
3. Tests → every test must pass
4. Commit → message: "refactor: <technique> <what>"
```
### If Tests Break
```
1. Undo the last change
2. Understand what broke and why
3. Try a smaller step
4. If the test was wrong and behavior was correct, fix the test FIRST, then retry
```
### On Completion
```
1. Full test suite → all tests pass
2. Manual smoke test → quick sanity check
3. Self-review diff → catch unintended changes
4. Final commit → describe the overall transformation
```
---
## Design Patterns in Refactoring
### Strategy Pattern
Replace a conditional that chooses an algorithm. **Smell:** Switch on type code with different behavior per branch. **Technique:** Replace Conditional with Polymorphism + Extract Method.
### Template Method
Extract common algorithm skeleton to superclass, letting subclasses fill in the variants. **Smell:** Duplicate code with slight variations. **Technique:** Form Template Method.
### State Pattern
Replace a state-based conditional by extracting each state's behavior into a class. **Smell:** Switch on status field with behavior variation. **Technique:** Replace Type Code with State/Strategy.
### Composite Pattern
Treat individual objects and groups uniformly. **Smell:** Client code has special handling for single vs. collection cases. **Technique:** Extract Interface + Create Composite.
### Decorator Pattern
Add behavior dynamically by wrapping objects. **Smell:** Conditional logic for optional behaviors. **Technique:** Extract Class + use composition.
### Null Object Pattern
Replace null checks with a default object. **Smell:** Repeated `if (x == null)` checks. **Technique:** Introduce Null Object.
---
## Edge Cases & Gotchas
| Scenario | Handling |
|----------|----------|
| No tests exist | Write characterization tests first. Run the code with various inputs, capture outputs. These are your safety net. |
| Refactoring breaks a distant test | FIRST understand why. Maybe the test relied on implementation detail. If so, fix the test to test behavior, not implementation. Then resume. |
| User wants behavior change + refactor together | REFUSE. Do them separately. Refactor first to make the behavior change easy, commit, then change behavior. |
| Method is too complex to step through | Use Replace Method with Method Object. Turn the whole method into a class where each step can be extracted. |
| Refactoring across a large codebase | Extract a micro-service or module boundary first. Then refactor within the boundary. "There is a refactoring for everything except too many refactorings." |
| IDE automated refactoring available | Use it. Modern IDEs can safely rename, extract method, introduce variable, etc. Only do it manually when the IDE can't. |
| Undo needed | `git stash` or `git reset --hard` back to last commit. Small commits make this painless. |
---
## Resources
- Martin Fowler, *Refactoring: Improving the Design of Existing Code* (2nd Edition, 2018)
- [refactoring.com](https://refactoring.com) — Fowler's online catalog
- [refactoring.guru](https://refactoring.guru) — Illustrated refactoring patterns
- [refactoring.guru/refactoring/catalog](https://refactoring.guru/refactoring/catalog) — full technique catalog (6 categories, 66 techniques) and code-smell taxonomy this skill mirrorsCreator's repository · smallnest/goal-workflow