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 s
By smallnest · 441 installs
npx skills add smallnest/goal-workflow --skill refactor
Source repository · Upstream listing
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
Refactoring Decision Rubric
Principles guide judgment; they are not independent reasons to rewrite code. Before choosing a Fowler technique, record one decision card:
Field Required answer
Primary smell One root cause, not one entry per principle
Evidence Location, behavior, callers, change history, or measurement
Primary principle The most specific applicable principle
Related principles Explanatory labels only; do not count separately
Expected impact Observable reduction in change spread, cognitive load, duplicated knowledge, coupling, delayed failure, or measured runtime cost
Smallest refactoring The least invasive Fowler technique that addresses the root cause
Baseline / success condition How behavior preservation and the expected benefit will be verified
Use these four decision lenses:
Lens Principles Questions to answer
Responsibility and dependencies SOLID, SRP, OCP, DIP, Separation of Concerns Are reasons to change mixed? Does a real new variant repeatedly modify stable logic? Does high level policy depend on concrete mechanism? Do concerns leak across a boundary?
Reuse and structure DRY, Composition Is the same knowledge duplicated, or merely similar syntax? Would composition localize a real variation better than inheritance?
Simplicity and scope KISS, YAGNI Is the proposed structure simpler for today's problem? Is every abstraction backed by an existing variation or boundary?
Runtime and feedback Fail Fast, Measure First Can invalid state fail nearer its source without changing error semantics? What baseline proves the problem and the result?
SOLID is an umbrella. When evidence supports SRP , OCP , or DIP , use that specific lens and do not create a second SOLID issue. LSP and ISP may be labeled SOLID/LSP and SOLID/ISP . DRY means shared knowledge, not all similar code. Composition is preferred when inheritance creates real coupling, not by default. OCP and DIP never justify speculative layers that violate KISS or YAGNI.
Numeric thresholds in this skill—line counts, parameter counts, method counts, and nesting depth—are context dependent candidate indicators . Confirm mixed responsibilities, cognitive cost, repeated change, duplicated knowledge, or measured runtime impact before acting.
Code Smells Catalog
Based on Fowler's taxonomy. Before refactoring, identify which smell is present.
Bloaters
Smell Description Primary Refactoring
Long Method Candidate: method 10 15 lines; confirm mixed responsibilities or cognitive cost Extract Method, Replace Temp with Query
Large Class Candidate: many fields/methods; confirm independent reasons to change 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 Candidate: 3 4 parameters; confirm a missing concept or recurring data clump 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:
After:
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:
After:
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:
After:
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 Associat