refactor

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.

Skill file

Preview skill file
---
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 mirrors

Source

Creator's repository · smallnest/goal-workflow

View on GitHub

Security

Security checks in progress
Results will appear here once audits complete
Checked by 3 independent security firms
Does it try to trick the AI?Not yet checkedPending · Gen Agent Trust Hub
Does it sneak in hidden code?Not yet checkedPending · Socket
Does it have known bugs?Not yet checkedPending · Snyk