Refactoring to Patterns
Refactoring to Patterns (Joshua Kerievsky, 2004) inverts the GoF approach.
Introduction
Refactoring to Patterns (Joshua Kerievsky, 2004) inverts the GoF approach. Don't design with patterns upfront — write the simplest code, smell-driven refactor in small steps, arrive at patterns when evidence demands them. Patterns become destinations, not starting points.
The story
Team A rewrote a 1,200-line OrderProcessor "with Strategy from day 1" — 8 strategies shipped, 2 ever used. Team B applied Kerievsky's path: Extract Method → Replace Conditional with Polymorphism → Strategy. They landed on 2 strategies evidence demanded, 600 focused lines, maintained for years.
The business problem
Upfront pattern design produces abstractions that don't match real variation:
- Unused strategies: 8 implementations, 2 used — maintenance tax forever.
- Big-bang refactors: break tests; stall feature delivery for quarters.
- Decoration patterns: introduced without smell context — readers confused.
- No path: teams can't step from messy code to patterned code safely.
The problem teams faced
Refactoring to Patterns fits when:
- Code smells exist — long methods, switch statements, duplicated logic.
- You need incremental steps with green tests between each move.
- Pattern name should emerge from refactor, not precede it.
- Team prefers evidence over "we might need SMS someday."
Understanding the topic
Intent: Reach patterns through Fowler's mechanical refactoring moves.
- Smell first — long switch, feature envy, shotgun surgery.
- Mechanical refactor — Extract Method, Move Method, Extract Class.
- Pattern emerges — Replace Conditional with Polymorphism → Strategy.
- Tests green — after every micro-step.
Internal architecture
OrderProcessor refactor sequence:
// Step 1: Extract Method from 200-line process()// Step 2: Replace Conditional with Polymorphisminterface ShippingCalculator { cost(order: Order): Money }class StandardShipping implements ShippingCalculator { ... }class ExpressShipping implements ShippingCalculator { ... }// Step 3: Strategy registry — only 2 variants evidence demandedconst calculators: Record<ShipMode, ShippingCalculator> = {standard: new StandardShipping(),express: new ExpressShipping(),}
Visual explanation
Three diagrams cover smell-to-pattern path, Kerievsky sequence, and upfront vs emergent:
Informative example
Before / after — upfront Strategy vs smell-driven:
// ❌ Upfront — 8 strategies, hypothetical variantsclass SmsStrategy {}class EmailStrategy {}class PushStrategy {} // never used// ... 5 more// ✅ Smell-driven — switch on ship mode firstfunction shippingCost(order: Order) {switch (order.mode) {case "standard": return order.weight * 0.5case "express": return order.weight * 2.0default: throw new Error("unknown mode")}}// Refactor when second variant proves real → Strategy with 2 impls
Execution workflow
Name the smell
Switch, long method, duplication.
Real-world use
IntelliJ/VS Code automated refactors; Kerievsky catalog; Feathers characterization tests; Martin Fowler Refactoring (2nd ed).
Production case study
OrderProcessor 1,200-line refactor:
- Team A: 8 strategies upfront — 6 dead code paths.
- Team B: smell-driven — 600 lines, 2 strategies.
- Lesson: patterns earned by evidence, not roadmap slides.
Trade-offs
- Pro: lean abstractions; tests guide each step; less dead code.
- Con: requires discipline — tempting to skip to pattern name.
- Con: slower initially than "just add Strategy."
Decision framework
- Start with simplest code that works.
- Refactor when smell appears twice — Rule of Three.
- Name pattern in ADR after refactor, not before.
Best practices
- One mechanical refactor per commit; tests green.
- Kerievsky catalog maps smell → refactor → pattern.
- Delete speculative interfaces in quarterly cleanup.
Anti-patterns to avoid
- "Design patterns first" greenfield — Team A trap.
- Big-bang rewrite to patterns without characterization tests.
- Keeping unused Strategy implementations "for the roadmap."
Common mistakes
- Naming Strategy before Replace Conditional is done — premature vocabulary.
- Skipping characterization on legacy — refactor blind.
Debugging tips
- When refactor breaks tests, revert one step — which move caused drift?
Optimization strategies
- IDE automated Extract Method/Interface — use tooling, don't hand-edit.
Common misconceptions
- Refactoring to Patterns ≠ no patterns — you arrive at them.
- Not anti-GoF — complements with evidence-based timing.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1AdvancedQuestionRefactoring to Patterns vs design patterns upfront?+
Answer
Follow-up
2IntermediateQuestionFirst refactor move on long switch?+
Answer
Follow-up
3AdvancedQuestionRole of characterization tests?+
Answer
Follow-up
Summary
You can apply Kerievsky's smell-driven path and tell the OrderProcessor story. If you can explain why Team B won, you own Refactoring to Patterns.