refactoring-patterns

$npx mdskill add wondelai/skills/refactoring-patterns

Apply behavior-preserving code transformations to eliminate code smells.

  • Identifies and resolves code smells like long methods or conditionals.
  • Relies on existing test suites to verify behavior preservation.
  • Selects appropriate refactoring patterns based on code smell analysis.
  • Outputs step-by-step transformation sequences with test verification.

SKILL.md

.github/skills/refactoring-patternsView on GitHub ↗
---
name: refactoring-patterns
description: 'Apply named refactoring transformations to improve code structure without changing behavior. Use when the user mentions "refactor this", "code smells", "extract method", "replace conditional", "technical debt", "move method", "inline variable", "decompose conditional", or "clean up this messy code". Also trigger when cleaning up legacy code, preparing code for new features by restructuring, or identifying which transformation fits a specific code smell. Covers smell-driven refactoring, safe transformation sequences, and testing guards. For code-quality foundations, see clean-code. For managing complexity, see software-design-philosophy.'
license: MIT
metadata:
  author: wondelai
  version: "1.4.1"
---

# Refactoring Patterns Framework

A disciplined approach to improving the internal structure of existing code without changing its observable behavior. Every refactoring follows the same loop: verify tests pass, apply one small structural change, verify tests still pass.

## Core Principle

**Refactoring is not rewriting. It is a sequence of small, behavior-preserving transformations, each backed by tests.** You never change what the code does — only how it is organized. Big-bang rewrites fail because they combine structural change with behavioral change, making it impossible to know which broke things.

**The foundation:** Bad code is a natural consequence of delivering under time pressure, not a character flaw. Code smells are objective signals of degraded structure; the smell catalog tells you *where* to look, and the refactoring catalog tells you *what to do*.

## Scoring

**Goal: 10/10.** Score structural quality by how many of the eight [Quick Diagnostic](#quick-diagnostic) rows pass — `score = round(passed / 8 × 10)`, adjusting down when a single smell is severe. Bands:
- **9-10**: no obvious smells remain, each function does one thing, names reveal intent, duplication is eliminated, conditionals use polymorphism where apt, and tests cover the refactored paths.
- **5-6**: a few smells remain (a Long Method, some duplication) but structure is mostly sound.
- **≤3**: pervasive smells — tangled conditionals, God classes, duplication everywhere — or no tests to refactor safely.

Always state the current score, name the smells driving it down, and list the specific refactorings needed to reach 10/10.

## The Refactoring Patterns Framework

Six areas of focus for systematically improving code structure:

### 1. Code Smells as Triggers

**Core concept:** Code smells are surface indicators of deeper structural problems — not bugs, but signals that the design makes code harder to understand, extend, or maintain. Each smell maps to named refactorings that fix it.

**Why it works:** Named smells give teams objective criteria instead of subjective "I don't like this" — "This is Feature Envy" points directly at the fix.

**Key insights:**
- Smells cluster into five families: Bloaters, Object-Orientation Abusers, Change Preventers, Dispensables, Couplers
- Long Method is the most common smell; Duplicate Code is the most expensive
- A method that needs a comment to explain *what* it does is a smell — extract and name the block instead
- Shotgun Surgery (one change, many classes) and Divergent Change (one class, many reasons to change) are opposite signals of misplaced responsibilities
- Primitive Obsession — raw strings/ints instead of small domain objects — spreads errors and duplication

**Code applications:**

| Context | Pattern | Example |
|---------|---------|---------|
| Method > 10 lines | Extract Method | Pull loop body into `calculateLineTotal()` |
| One change touches many classes (Shotgun Surgery) | Move Method/Field | Gather the scattered behavior into one class |
| Same params in many methods | Introduce Parameter Object | `startDate, endDate` → `DateRange` |
| Copy-pasted logic | Extract Method + Pull Up Method | Share via common method or base class |

See [references/smell-catalog.md](references/smell-catalog.md) when you need to name a smell and its fix — all five families (Bloaters, OO Abusers, Change Preventers, Dispensables, Couplers) with detection heuristics and the refactoring each maps to.

### 2. Composing Methods

**Core concept:** Most refactoring starts here: break long methods into smaller, well-named pieces that read like prose — high-level steps delegating to clearly named helpers.

**Why it works:** Short methods with intention-revealing names eliminate comments, make bugs obvious at a glance, and enable reuse; a method call costs nothing to read when the name says everything.

**Key insights:**
- Extract Method is the single most important refactoring — master it first
- Urge to write a comment? Extract the block and use the comment as the method name
- Inline Method when the body is as clear as the name — indirection without value is noise
- Replace Temp with Query for computed values used in multiple places; Split Temporary Variable when one temp serves two purposes
- Replace Method with Method Object when locals are too tangled to extract — they become fields

**Code applications:**

| Context | Pattern | Example |
|---------|---------|---------|
| Block with a comment | Extract Method | `// check eligibility` → `isEligible()` |
| Temp used once | Inline Variable | Drop `const price = order.getPrice()` |
| Trivial delegating method | Inline Method | Inline `return deliveries > 5` if used once |
| Method with many tangled locals | Replace Method with Method Object | Locals become fields in a new class |

See [references/composing-methods.md](references/composing-methods.md) when applying any method-level transformation — step-by-step mechanics and before/after code for Extract/Inline Method, Extract/Inline Variable, Replace Temp with Query, Split Temporary Variable, and Replace Method with Method Object.

### 3. Moving Features Between Objects

**Core concept:** The key OO design decision is where responsibilities live. When Feature Envy, excessive coupling, or unbalanced class sizes show a method or field is in the wrong class, move it where it belongs.

**Why it works:** A method placed away from the data it uses creates invisible cross-class dependencies, so one logical change ripples across many files — Shotgun Surgery. Co-locating method and data confines the change to one class.

**Key insights:**
- Move Method when a method uses more of another class's features than its own; Move Field likewise
- Extract Class when one class does two things — split along the axis of change; Inline Class when one does too little
- Hide Delegate enforces the Law of Demeter; Remove Middle Man undoes it when forwarding becomes the whole class
- Resolve that tension case by case: hide the delegate when the chain is unstable, remove the middle man when it's pure forwarding

**Code applications:**

| Context | Pattern | Example |
|---------|---------|---------|
| Method envies another class | Move Method | `calculateShipping()` from `Order` to `ShippingPolicy` |
| God class 500+ lines | Extract Class | Pull `Address` fields/methods into own class |
| Client calls `a.getB().getC()` | Hide Delegate | Add `a.getCThroughB()` |
| Class only forwards calls | Remove Middle Man | Let client call the delegate directly |

See [references/moving-features.md](references/moving-features.md) when deciding where a responsibility belongs — mechanics for Move Method/Field, Extract/Inline Class, Hide Delegate, and Remove Middle Man.

### 4. Organizing Data

**Core concept:** Raw data — magic numbers, exposed fields, integer type codes — creates subtle bugs and scatters domain knowledge. Replace primitives with objects that encapsulate behavior and enforce invariants.

**Why it works:** An `int` amount has no rounding rules or currency code; a `Money` object encapsulates all of it, so business rules live in one place and the type system catches errors at compile time.

**Key insights:**
- Replace Magic Number with Symbolic Constant — the simplest data refactoring; it names intent
- Replace Data Value with Object cures Primitive Obsession (`EmailAddress`, `Money`, `Temperature`)
- Encapsulate Field and Encapsulate Collection — never expose raw fields or mutable internal lists
- Replace Type Code with Subclasses when the code affects behavior; with Strategy when subclassing is impractical
- Change Value to Reference when you need identity semantics (one shared `Customer`, not copies)

**Code applications:**

| Context | Pattern | Example |
|---------|---------|---------|
| `if (status == 2)` | Replace Magic Number | `if (status == ORDER_SHIPPED)` |
| `String email` passed everywhere | Replace Data Value with Object | `EmailAddress` class with validation |
| Getter returns mutable list | Encapsulate Collection | Return `Collections.unmodifiableList(items)` |
| `int typeCode` with switch | Replace Type Code with Subclasses | `Employee` → `Engineer`, `Manager` |

See [references/organizing-data.md](references/organizing-data.md) when replacing primitives with objects — mechanics for Replace Data Value with Object, Change Value to Reference, Replace Magic Number, Encapsulate Field/Collection, and the Replace Type Code variants.

### 5. Simplifying Conditional Logic

**Core concept:** Deeply nested if/else trees, long switches, and scattered null checks are the hardest code to read and the most bug-prone. Named refactorings decompose, consolidate, and replace conditionals with clearer structures.

**Why it works:** A six-branch conditional forces readers to simulate every path mentally; well-named extracted branches are self-documenting, and polymorphism eliminates whole categories of "forgot this case" bugs.

**Key insights:**
- Decompose Conditional: extract condition, then-branch, and else-branch into named methods
- Consolidate Conditional Expression: merge conditions with the same result into one named check
- Replace Nested Conditional with Guard Clauses: handle edge cases early and return, keeping the main path unindented
- Replace Conditional with Polymorphism is the gold standard for type-based conditionals
- Introduce Special Case (Null Object) eliminates scattered `if (x == null)` checks; Introduce Assertion makes assumptions fail fast

**Code applications:**

| Context | Pattern | Example |
|---------|---------|---------|
| Long `if` with complex condition | Decompose Conditional | Extract `isSummer(date)` and `summerCharge()` |
| Deeply nested `if/else` | Replace with Guard Clauses | Edge cases first, return early, flat main path |
| Switch on object type | Replace Conditional with Polymorphism | Each type implements its own `calculatePay()` |
| `if (customer == null)` everywhere | Introduce Special Case | `NullCustomer` with safe default behavior |

See [references/simplifying-conditionals.md](references/simplifying-conditionals.md) when untangling branches — before/after examples for Decompose/Consolidate Conditional, Guard Clauses, Replace Conditional with Polymorphism, Special Case, and Assertions.

### 6. Safe Refactoring Workflow

**Core concept:** Refactoring is only safe when wrapped in tests. The workflow is mechanical: run tests (green), apply one small transformation, run tests (green), commit. If tests go red, revert — don't debug a broken refactoring.

**Why it works:** Small steps make the failure obvious (it was the last thing you did) and reverting costs seconds; debugging a failed big-bang rewrite costs days.

**Key insights:**
- Rule of Three: tolerate duplication once, note it twice, refactor on the third occurrence
- Preparatory refactoring: restructure to make the feature easy *before* adding it; comprehension and litter-pickup refactoring keep code improving as you read and touch it
- When NOT to refactor: rewriting is easier, no tests and adding them isn't feasible, or the code will be deleted soon
- Refactor for clarity first, then profile and optimize the measured bottleneck — clear code is easier to tune
- Branch by Abstraction and Parallel Change enable large refactorings in production without long-lived branches

**Code applications:**

| Context | Pattern | Example |
|---------|---------|---------|
| About to add a feature | Preparatory Refactoring | Clean the insertion point first |
| Third copy of same logic | Rule of Three | Extract shared logic now |
| Large API change in production | Branch by Abstraction | Add abstraction layer, migrate callers, remove old path |
| Renaming a widely-used method | Parallel Change | Add new, deprecate old, migrate, remove |

See [references/refactoring-workflow.md](references/refactoring-workflow.md) before a large or risky refactoring — the full green-to-green cycle, when (not) to refactor, performance, Branch by Abstraction, and Parallel Change.

## Common Mistakes

| Mistake | Why It Fails | Fix |
|---------|-------------|-----|
| Refactoring without tests | No safety net to detect behavior change | Write characterization tests first |
| Big-bang rewrite | Mixes structural and behavioral change; undebuggable | Smallest possible steps, tests after each |
| Refactoring while adding features | Two hats at once — neither change verifiable | Refactor first (commit), then add feature (commit) |
| Renaming without updating callers | Broken build or dead code | Use IDE rename; search all references |
| Extracting too many tiny methods | Indirection without clarity when names are poor | Each name must remove the need to read the body |
| Ignoring the smell catalog | Reinvents fixes instead of applying proven recipes | Learn named smells; each maps to refactorings |
| Refactoring doomed code | Polish on condemned code is waste | Check the code's lifespan justifies the investment |
| Optimizing while refactoring | Conflates clarity with performance | Clarity first, then profile, then optimize hot path |

## Quick Diagnostic

| Question | If No | Action |
|----------|-------|--------|
| Do tests pass before you start? | No safety net | Write or fix tests first — never refactor red |
| Can you name the smell you're fixing? | Refactoring by instinct, not catalog | Identify the smell, apply its prescribed refactoring |
| Is each method under ~10 lines? | Long Methods likely | Extract Method into named steps |
| Does each class have one reason to change? | Divergent Change or Large Class | Extract Class to separate responsibilities |
| Are there duplicated code blocks? | The most expensive smell | Extract shared logic into common method/base class |
| Do conditionals use polymorphism where apt? | Switch Statements remain | Replace Conditional with Polymorphism |
| Are you committing after each step? | Risk losing work, mixing changes | Commit after every green-to-green transformation |
| Is the code easier to read after your change? | Refactoring added complexity | Revert and try a different approach |

## Further Reading

The definitive guides to improving existing code:

- [*"Refactoring: Improving the Design of Existing Code (2nd Edition)"*](https://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599?tag=wondelai00-20) by Martin Fowler
- [*"Working Effectively with Legacy Code"*](https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052?tag=wondelai00-20) by Michael Feathers (companion for code without tests)
- [*"Clean Code: A Handbook of Agile Software Craftsmanship"*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882?tag=wondelai00-20) by Robert C. Martin (complementary naming and style principles)

## About the Author

**Martin Fowler** is Chief Scientist at Thoughtworks, a signatory of the Agile Manifesto, and author of *Refactoring: Improving the Design of Existing Code* (1999; 2nd edition 2018), which introduced catalog-based, named refactorings to mainstream development. His catalog underpins the automated refactoring tools in every major IDE.

More from wondelai/skills

SkillDescription
37signals-wayBuild lean, opinionated products using the 37signals philosophy from "Getting Real", "Rework", and "Shape Up". Use when the user mentions "Getting Real", "Rework", "Shape Up", "37signals", "Basecamp method", "six-week cycles", "fixed time variable scope", "appetite vs estimates", "betting table", "breadboarding", "fat marker sketch", "build less", "underdo the competition", "opinionated software", "we have too many meetings", "how do we ship faster", or "stop overbuilding". Also trigger when cutting scope to ship sooner, running a small team, or avoiding long-term roadmaps. Covers shaping, betting, building, and the art of saying no. For MVP validation, see lean-startup. For design sprints, see design-sprint.
blue-ocean-strategyCreate uncontested market space using value innovation instead of competing head-to-head. Use when the user mentions "blue ocean", "red ocean", "strategy canvas", "ERRC framework", "value innovation", "non-customers", "buyer utility map", "the market is too crowded", "how do we stand out", or "escape the price war". Also trigger when exploring a new market category, or finding underserved or non-customers. Covers the Four Actions Framework, Six Paths, buyer utility map, and value-cost trade-offs. For real strategy formulation and bad-strategy detection, see good-strategy-bad-strategy. For tech adoption strategy, see crossing-the-chasm. For product positioning, see obviously-awesome.
clean-architectureStructure software around the Dependency Rule: source code dependencies point inward from frameworks to use cases to entities. Use when the user mentions "architecture layers", "dependency rule", "ports and adapters (hexagonal)", "onion architecture", "screaming architecture", "where should business logic go", "decouple from the database", "swap the framework without a rewrite", or "keep business rules independent". Also trigger when deciding which layer code belongs in, isolating core logic from infrastructure, defining module boundaries, or debating whether the framework should call your code or the reverse. Covers component principles, boundaries, and SOLID. For code-level quality, see clean-code. For domain modeling, see domain-driven-design.
clean-codeWrite readable, maintainable code through disciplined naming, small functions, and clean error handling. Use when the user mentions "clean up this code", "this function is too long", "code smells", "naming conventions", "boy scout rule", "single responsibility", or "unit test quality". Also trigger when reviewing a pull request for readability, untangling a messy function, debating comment styles, or improving error-handling patterns. Covers SRP, comment discipline, formatting, and unit testing. For refactoring techniques, see refactoring-patterns. For architecture and dependency rules, see clean-architecture.
cold-start-problemStart and scale networked products using Andrew Chen''s "The Cold Start Problem" framework for network effects. Use when the user mentions "network effects", "chicken and egg", "cold start", "two-sided marketplace", "atomic network", "hard side", "liquidity", "critical mass", "invite-only launch", "how do I get my first users", or "the marketplace has no buyers or sellers". Also trigger when launching a marketplace, social, or collaboration product that is worthless without other users, deciding launch sequencing and seeding tactics, or diagnosing stalled network growth at scale. Covers the five stages: cold start, tipping point, escape velocity, hitting the ceiling, and the moat. For word-of-mouth virality, see contagious. For habit-driven retention, see hooked-ux.
contagiousEngineer word-of-mouth and virality using the STEPPS framework (Social Currency, Triggers, Emotion, Public, Practical Value, Stories). Use when the user mentions "go viral", "word of mouth", "shareable content", "social currency", "why people share", "referral program", "nobody is sharing it", or "make this spread". Also trigger when designing shareable features, crafting social campaigns, or building products that spread through peer recommendation. Covers environmental triggers and high-arousal emotional content. For sticky messaging, see made-to-stick. For persuasion tactics, see influence-psychology.
continuous-discoveryBuild a weekly cadence of customer touchpoints using Opportunity Solution Trees, assumption mapping, and interview snapshots. Use when the user mentions "continuous discovery", "opportunity solution tree", "weekly interviews", "assumption testing", "discovery habits", "product trio", "outcome-based roadmap", "how do I talk to customers regularly", "we keep building things nobody uses", or "connect research to the roadmap". Also trigger when setting up regular customer feedback loops, prioritizing which experiments to run, or tying discovery insights to delivery work. Covers experience mapping, co-creation, and prioritizing opportunities. For interview technique, see mom-test. For team structure, see inspired-product.
create-appGuided journey from a raw app idea to a validated, cleanly architected first version that ships on a sustainable cadence. Orchestrates ten skills phase by phase - lean-startup, design-sprint, clean-architecture, domain-driven-design, clean-code, pragmatic-programmer, system-design, ios-hig-design, 37signals-way, software-design-philosophy - asking the user questions at every decision point and recording results in the project docs/ folder (PRODUCT.md, ARCHITECTURE.md, EXPERIMENTS.md, CREATE-APP-PLAN.md) so the journey resumes across sessions. Use when the user wants to build a new app, validate an idea before writing code, architect an MVP that will not need a rewrite, or says ''help me build my app the right way''. App already exists: use improve-app or grow-app. Business idea not yet validated: run create-business first. Marketing site only: use create-website. Architecture-only question: design-code-architecture. For one framework in isolation, invoke that skill directly.
create-businessGuided journey from raw idea to a validated, positioned, priced business with a chosen beachhead. Orchestrates ten skills phase by phase - jobs-to-be-done, mom-test, design-sprint, lean-startup, good-strategy-bad-strategy, blue-ocean-strategy, obviously-awesome, hundred-million-offers, monetizing-innovation, crossing-the-chasm - asking the user questions at every decision point and recording results in the project docs/ folder (CUSTOMER.md, POSITIONING.md, OFFER.md, CREATE-BUSINESS-PLAN.md) so the journey resumes across sessions. Use when the user wants to start a new business, validate a startup idea, find product-market fit before building, or says ''I have an idea for a company''. Do not use once the business has paying customers: use grow-business to add revenue and customers, or improve-business to fix strategy and operations. For building the product itself use create-app or create-website. For one framework in isolation, invoke that skill directly.
create-websiteGuided journey from a blank page to a live, high-converting website, built message-first, then design, then conversion. Orchestrates ten skills phase by phase - one-page-marketing, storybrand-messaging, made-to-stick, top-design, web-typography, refactoring-ui, ux-heuristics, cro-methodology, scorecard-marketing, steve-jobs-design-review - asking the user questions at every decision point and recording results in the project docs/ folder (POSITIONING.md, WEBSITE.md, DESIGN.md, CREATE-WEBSITE-PLAN.md) so the journey resumes across sessions. Use when the user wants to build a new website or landing page from scratch, turn a validated idea into a page that converts, or says ''build me a landing page for my SaaS''. Site already exists: use improve-website to fix conversion/UX or grow-website for more traffic; building a software product, not a marketing site: use create-app; no positioning yet: run create-business first or the positioning phase here. For one framework in isolation, invoke that skill directly.