Refactoring Skill
Structured approach to technical debt remediation. Improve code structure while maintaining behavior through incremental, safe changes.
When to Use
Trigger Description
Guardrail Violations Functions >50 lines, files >300 lines
Complexity Threshold Cyclomatic complexity >10
Code Duplication Same logic in 3+ places
Quarterly Review Regular technical debt assessment
Pre-Feature Before adding features to messy code
Post-Incident After bugs caused by confusing code
Refactoring Principles
Golden Rules
-
Behavior Preservation: Output unchanged after refactoring
-
Small Steps: One change at a time
-
Test First: Ensure tests exist before changing
-
Incremental: Commit after each successful change
-
Reversible: Easy to roll back if issues arise
When NOT to Refactor
-
During active incident response
-
Without test coverage
-
When deadline is imminent
-
If no clear improvement goal
Prerequisites
Before starting:
-
Code compiles and all tests pass
-
Test coverage exists for target code (>60%)
-
Clear refactoring goal defined
-
Time allocated (refactoring always takes longer)
-
Git working directory is clean
Refactoring Process
Phase 1: Identify Candidates ↓ Phase 2: Impact Analysis ↓ Phase 3: Plan Refactoring ↓ Phase 4: Add Safety Net ↓ Phase 5: Execute Incrementally ↓ Phase 6: Validate & Document
Phase 1: Identify Candidates
1.1 Automated Detection
Long Functions (>50 lines):
Find functions over 50 lines (varies by language)
Example: use linter rules or IDE features
Long Files (>300 lines):
Find files over 300 lines
find src -name ".ts" -exec wc -l {} ; | awk '$1 > 300' find src -name ".py" -exec wc -l {} ; | awk '$1 > 300'
High Complexity:
Use complexity analyzers
npx escomplex src//*.ts python -m mccabe src//*.py
1.2 Code Smells
Smell Signs Priority
Long Method
50 lines, multiple responsibilities High
Large Class
300 lines, low cohesion High
Feature Envy Method uses other class more than own Medium
Data Clumps Same group of variables together Medium
Primitive Obsession Overuse of primitives vs objects Medium
Switch Statements Large switch blocks Low
Speculative Generality Unused abstractions Low
1.3 Candidate List
Document identified candidates:
Refactoring Candidates
High Priority
| File | Issue | Lines | Impact |
|---|---|---|---|
| src/services/order.ts | Long method: processOrder | 120 → 50 | High |
| src/api/users.ts | Large file | 450 → 200 | High |
| src/utils/validation.ts | Duplication | N/A | Medium |
Medium Priority
| File | Issue | Lines | Impact |
|---|---|---|---|
| src/models/product.ts | Feature envy | N/A | Medium |
| src/services/email.ts | Complex conditionals | N/A | Medium |
Phase 2: Impact Analysis
2.1 Dependency Mapping
For each candidate, identify:
Target: src/services/order.ts
Dependencies (imports from):
- src/models/order.ts
- src/services/inventory.ts
- src/services/payment.ts
Dependents (imported by):
- src/api/orders.ts
- src/workers/orderProcessor.ts
- tests/services/order.test.ts
Impact radius: 5 files Risk level: Medium
2.2 Risk Assessment
Factor Low Medium High
Test coverage
80% 60-80% <60%
Dependents <3 3-10
10
Business criticality Non-critical Important Critical path
Complexity Simple rename Extract method Architecture change
2.3 Risk Matrix
Low Effort High Effort
┌────────────┬────────────┐
High Value │ DO FIRST │ PLAN │ │ │ CAREFULLY │ ├────────────┼────────────┤ Low Value │ QUICK WIN │ AVOID │ │ │ │ └────────────┴────────────┘
Phase 3: Plan Refactoring
3.1 Choose Refactoring Technique
Smell Technique Description
Long method Extract Method Pull out logical chunks
Large class Extract Class Split by responsibility
Feature envy Move Method Move to appropriate class
Data clumps Extract Parameter Object Group related params
Duplicated code Extract to shared function DRY principle
Complex conditional Replace with polymorphism Strategy pattern
Long parameter list Introduce Parameter Object Create wrapper class
3.2 Step-by-Step Plan
Example: Extract Method from Long Function
Refactoring Plan: processOrder()
Goal
Reduce processOrder() from 120 lines to <50 lines
Steps
- Add characterization tests for current behavior
- Extract validateOrder() (lines 15-35)
- Verify tests pass
- Commit: "refactor: extract validateOrder"
- Extract calculateTotals() (lines 40-65)
- Verify tests pass
- Commit: "refactor: extract calculateTotals"
- Extract processPayment() (lines 70-95)
- Verify tests pass
- Commit: "refactor: extract processPayment"
- Extract sendConfirmation() (lines 100-115)
- Verify tests pass
- Commit: "refactor: extract sendConfirmation"
- Final cleanup and documentation
- Commit: "refactor: cleanup processOrder"
Expected Result
- processOrder(): 120 → 25 lines
- New methods: 4
- Total lines: +10 (net increase for clarity)
Phase 4: Add Safety Net
4.1 Characterization Tests
Before refactoring, add tests that capture current behavior:
describe('processOrder - characterization tests', () => { it('should match current behavior for valid order', () => { const order = createValidOrder(); const result = processOrder(order);
// Capture current output exactly
expect(result).toMatchSnapshot();
});
it('should match current behavior for edge cases', () => { const edgeCases = [ createEmptyOrder(), createMaxItemOrder(), createDiscountedOrder(), ];
edgeCases.forEach(order => {
expect(processOrder(order)).toMatchSnapshot();
});
}); });
4.2 Coverage Check
Ensure adequate coverage before proceeding:
Check coverage for target file
npm test -- --coverage --collectCoverageFrom="src/services/order.ts"
Minimum Coverage: 60% before refactoring (prefer >80%)
4.3 Checkpoint Commit
Create a checkpoint before starting:
git add . git commit -m "chore: checkpoint before refactoring processOrder"
Phase 5: Execute Incrementally
5.1 One Change at a Time
Bad: Multiple changes in one commit
Too much at once - hard to debug if tests fail
git commit -m "refactor: refactor entire order module"
Good: Atomic changes
git commit -m "refactor(order): extract validateOrder method" git commit -m "refactor(order): extract calculateTotals method" git commit -m "refactor(order): extract processPayment method"
5.2 Red-Green-Refactor
For each change:
- Run tests → PASS (green)
- Make one refactoring change
- Run tests → Should still PASS (green)
- If FAIL (red) → Revert and try smaller change
- If PASS → Commit
- Repeat
5.3 Common Refactoring Patterns
See references/process.md for detailed code examples including:
-
Extract Method pattern
-
Extract Class pattern
-
Replace Conditional with Polymorphism pattern
Phase 6: Validate & Document
6.1 Final Validation
-
All tests pass
-
Coverage maintained or improved
-
No new linter warnings
-
Performance unchanged (or improved)
-
Behavior identical (check snapshots)
6.2 Document Changes
Update patterns.md (if new pattern emerged):
Order Processing Pattern
Orders are processed through composable steps:
- validateOrder() - Input validation
- calculateTotals() - Price calculations
- processPayment() - Payment handling
- sendConfirmation() - Notifications
Create memory entry (if significant):
Refactoring: Order Processing
Date: 2025-01-15 Files: src/services/order.ts
Before
- Single 120-line function
- Difficult to test
- Hard to modify
After
- 5 focused functions (<30 lines each)
- Easier to test
- Clear responsibilities
Lessons
- Extract Method is low-risk, high-reward
- Characterization tests prevented regressions
6.3 Final Commit
git add . git commit -m "refactor(order): complete processOrder decomposition
- Extract validateOrder (20 lines)
- Extract calculateTotals (25 lines)
- Extract processPayment (30 lines)
- Extract sendConfirmation (20 lines)
- Main function now 25 lines (was 120)
No behavior changes. All tests passing."
Refactoring Catalog
Quick Reference
Refactoring When Effort Risk
Rename Unclear naming Low Low
Extract Method Long function Low Low
Extract Variable Complex expression Low Low
Inline Over-abstraction Low Low
Extract Class Large class Medium Medium
Move Method Feature envy Medium Medium
Extract Parameter Object Long param list Medium Low
Replace Conditional with Polymorphism Complex switch High Medium
Replace Inheritance with Composition Rigid hierarchy High High
IDE Support
Most refactorings have IDE shortcuts:
Action VS Code JetBrains
Rename F2 Shift+F6
Extract Method Ctrl+Shift+R Ctrl+Alt+M
Extract Variable Ctrl+Shift+R Ctrl+Alt+V
Move Drag or F2 F6
Inline N/A Ctrl+Alt+N
Checklist
Before Refactoring
-
Clear goal defined
-
Tests exist (>60% coverage)
-
Impact analysis complete
-
Step-by-step plan created
-
Checkpoint committed
During Refactoring
-
One change at a time
-
Tests run after each change
-
Commit after each successful change
-
No behavior changes introduced
After Refactoring
-
All tests pass
-
Coverage maintained
-
Code cleaner (measurable)
-
Documentation updated
-
Patterns documented (if applicable)
Related Resources
-
Workflows: code-review.md, testing-strategy.md, troubleshooting.md
-
Detailed Examples: See references/process.md for code patterns
-
Refactoring Book: Martin Fowler's "Refactoring: Improving the Design of Existing Code"