Design Patterns Tutorial 0/70 lessons ~6 min read Lesson 56

    Refactoring to Patterns

    Refactoring to Patterns (Joshua Kerievsky, 2004) inverts the GoF approach.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    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:

    ts
    // Step 1: Extract Method from 200-line process()
    // Step 2: Replace Conditional with Polymorphism
    interface ShippingCalculator { cost(order: Order): Money }
    class StandardShipping implements ShippingCalculator { ... }
    class ExpressShipping implements ShippingCalculator { ... }
    // Step 3: Strategy registry — only 2 variants evidence demanded
    const 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:

    Smell → refactor → pattern
    Long switch
    Smell
    Extract Method
    Fowler move
    Replace Conditional
    Polymorphism
    Strategy emerges
    Evidence
    Pattern is destination — not departure point.
    Upfront vs emergent Strategy
    Day 1: 8 strategies
    Team A
    2 ever used
    Waste
    Smell-driven refactor
    Team B
    2 strategies remain
    Evidence
    OrderProcessor story — same domain, opposite approach.
    Green tests between steps
    Characterization
    Lock behavior
    Extract Method
    Commit
    Tests green
    Always
    Next move
    Small step
    Never big-bang refactor — micro-steps with safety net.

    Informative example

    Before / after — upfront Strategy vs smell-driven:

    ts
    // ❌ Upfront — 8 strategies, hypothetical variants
    class SmsStrategy {}
    class EmailStrategy {}
    class PushStrategy {} // never used
    // ... 5 more
    // ✅ Smell-driven — switch on ship mode first
    function shippingCost(order: Order) {
    switch (order.mode) {
    case "standard": return order.weight * 0.5
    case "express": return order.weight * 2.0
    default: throw new Error("unknown mode")
    }
    }
    // Refactor when second variant proves real → Strategy with 2 impls

    Execution workflow

    1Refactoring to Patterns workflow
    1 / 5

    Name the smell

    Switch, long method, duplication.

    Don't name pattern yet.

    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.

    3 questions
    1AdvancedQuestionRefactoring to Patterns vs design patterns upfront?+

    Answer

    Kerievsky: smell → mechanical refactor → pattern emerges. GoF upfront often creates unused abstractions. Both valid when variation is proven.

    Follow-up

    OrderProcessor story?
    2IntermediateQuestionFirst refactor move on long switch?+

    Answer

    Often Extract Method for each branch, then Replace Conditional with Polymorphism when branches represent types. Don't jump to Strategy interface on day one.

    Follow-up

    When Strategy name?
    3AdvancedQuestionRole of characterization tests?+

    Answer

    Pin legacy behavior before refactor — Feathers. Refactoring to Patterns assumes green tests between every micro-step.

    Follow-up

    Legacy without tests?

    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.

    Ready to mark this lesson complete?Track your journey across the entire course.