Claude Cursor GitHub Copilot Skill

refactoring-patterns

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

LLM Mart · 0 points · 3 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download wondelai-skills-plugins_wondelai-skills_skills_refactoring-patterns-c172996.zip · 34 KB
Part of wondelai/skills — 183 skills

Install

skills CLI npx skills add https://github.com/wondelai/skills/tree/main/plugins/wondelai-skills/skills/refactoring-patterns
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install wondelai-skills@llmmart
Git git clone https://github.com/wondelai/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole wondelai/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

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 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 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 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 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 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 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 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:

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.

Files (skills)
  • references
    • composing-methods.md 11.6 KB
      # Composing Methods
      
      Detailed reference for the refactorings that break down long methods into well-named, cohesive pieces. These are the most frequently used refactorings and the foundation of all code improvement.
      
      ## Table of Contents
      1. [Extract Method](#extract-method)
      2. [Inline Method](#inline-method)
      3. [Extract Variable](#extract-variable)
      4. [Inline Variable](#inline-variable)
      5. [Replace Temp with Query](#replace-temp-with-query)
      6. [Split Temporary Variable](#split-temporary-variable)
      7. [Remove Assignments to Parameters](#remove-assignments-to-parameters)
      8. [Replace Method with Method Object](#replace-method-with-method-object)
      9. [Decision Guide: Which Composing Refactoring to Use](#decision-guide-which-composing-refactoring-to-use)
      
      ---
      
      ## Extract Method
      
      The single most important refactoring. Turn a code fragment into a method whose name explains the purpose of the fragment.
      
      ### Motivation
      
      You have a code fragment that can be grouped together. The longer a method, the harder it is to understand. When you see a comment explaining what the next block does, that's a signal to extract. The comment becomes the method name.
      
      ### Mechanics
      
      1. Create a new method and name it after the *intention* of the code (what it does, not how)
      2. Copy the extracted code into the new method
      3. Scan the extracted code for references to local variables -- these become parameters or return values
      4. Declare any local-scope variables as local to the new method if they're only used within the extracted code
      5. Pass any remaining local variables as parameters
      6. If more than one value needs to be returned, consider returning an object or extracting further
      7. Replace the original code with a call to the new method
      8. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      function printOwing(invoice) {
        let outstanding = 0;
      
        // print banner
        console.log("***********************");
        console.log("**** Customer Owes ****");
        console.log("***********************");
      
        // calculate outstanding
        for (const order of invoice.orders) {
          outstanding += order.amount;
        }
      
        // print details
        console.log(`name: ${invoice.customer}`);
        console.log(`amount: ${outstanding}`);
        console.log(`due: ${invoice.dueDate.toLocaleDateString()}`);
      }
      ```
      
      **After:**
      ```javascript
      function printOwing(invoice) {
        printBanner();
        const outstanding = calculateOutstanding(invoice);
        printDetails(invoice, outstanding);
      }
      
      function printBanner() {
        console.log("***********************");
        console.log("**** Customer Owes ****");
        console.log("***********************");
      }
      
      function calculateOutstanding(invoice) {
        let result = 0;
        for (const order of invoice.orders) {
          result += order.amount;
        }
        return result;
      }
      
      function printDetails(invoice, outstanding) {
        console.log(`name: ${invoice.customer}`);
        console.log(`amount: ${outstanding}`);
        console.log(`due: ${invoice.dueDate.toLocaleDateString()}`);
      }
      ```
      
      ### Naming Guidelines
      
      | Bad Name | Good Name | Why |
      |----------|-----------|-----|
      | `doStuff()` | `calculateMonthlyTotal()` | Names the intent, not the vagueness |
      | `process()` | `validateAndSaveOrder()` | Specific about what it does |
      | `handleData()` | `parseCSVRow()` | Names the domain concept |
      | `helper()` | `formatCurrencyForDisplay()` | Describes the transformation |
      | `step2()` | `applyDiscountRules()` | Names the business concept |
      
      **Rule of thumb:** If you can't find a good name, the extraction boundaries may be wrong. Try extracting a different fragment.
      
      ---
      
      ## Inline Method
      
      The inverse of Extract Method. Replace a method call with the method's body when the body is as clear as the name, or when you need to regroup poorly factored code.
      
      ### Motivation
      
      Sometimes a method body is as obvious as the method name. Indirection without value is noise. Also useful as an intermediate step: inline a badly decomposed method, then re-extract along better boundaries.
      
      ### Mechanics
      
      1. Check that the method is not polymorphic (no subclass overrides it)
      2. Find all callers
      3. Replace each call with the method body
      4. Delete the method
      5. Run tests
      
      ### Example
      
      **Before:**
      ```python
      def get_rating(self):
          return 2 if self.more_than_five_late_deliveries() else 1
      
      def more_than_five_late_deliveries(self):
          return self.late_deliveries > 5
      ```
      
      **After:**
      ```python
      def get_rating(self):
          return 2 if self.late_deliveries > 5 else 1
      ```
      
      ### When NOT to Inline
      
      - When the method name communicates domain meaning the code doesn't
      - When the method is used in multiple places (DRY)
      - When the method is overridden in subclasses
      
      ---
      
      ## Extract Variable
      
      Introduce a local variable for a complex expression to make it self-documenting.
      
      ### Motivation
      
      Expressions can become hard to read. A well-named variable for a sub-expression acts as inline documentation and makes debugging easier.
      
      ### Mechanics
      
      1. Identify a complex expression or sub-expression
      2. Declare a variable named for the intent of the expression
      3. Replace the expression with the variable
      4. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      return order.quantity * order.itemPrice -
        Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 +
        Math.min(order.quantity * order.itemPrice * 0.1, 100);
      ```
      
      **After:**
      ```javascript
      const basePrice = order.quantity * order.itemPrice;
      const quantityDiscount = Math.max(0, order.quantity - 500) * order.itemPrice * 0.05;
      const shippingCap = Math.min(basePrice * 0.1, 100);
      return basePrice - quantityDiscount + shippingCap;
      ```
      
      ---
      
      ## Inline Variable
      
      The inverse of Extract Variable. Remove a variable when the expression is just as clear.
      
      ### When to Use
      
      - The variable name adds no information beyond what the expression says
      - The variable is assigned once and used once
      - The variable is blocking another refactoring (e.g., you need to inline it to then Extract Method)
      
      ### Example
      
      **Before:**
      ```python
      base_price = order.base_price()
      return base_price > 1000
      ```
      
      **After:**
      ```python
      return order.base_price() > 1000
      ```
      
      ---
      
      ## Replace Temp with Query
      
      Turn a temporary variable into a method call so the computation is reusable and the original method becomes shorter.
      
      ### Motivation
      
      Temporaries can only be seen within a single method. If the same computation is needed elsewhere, it gets duplicated. A query method is visible to the whole class (or can be extracted to another class).
      
      ### Mechanics
      
      1. Check that the variable is assigned once and the expression has no side effects
      2. Extract the right-hand side of the assignment into a new method
      3. Replace all references to the temp with calls to the new method
      4. Remove the temp declaration and assignment
      5. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      class Order {
        getPrice() {
          const basePrice = this.quantity * this.itemPrice;
          if (basePrice > 1000) {
            return basePrice * 0.95;
          } else {
            return basePrice * 0.98;
          }
        }
      }
      ```
      
      **After:**
      ```javascript
      class Order {
        getPrice() {
          if (this.basePrice() > 1000) {
            return this.basePrice() * 0.95;
          } else {
            return this.basePrice() * 0.98;
          }
        }
      
        basePrice() {
          return this.quantity * this.itemPrice;
        }
      }
      ```
      
      ### Performance Note
      
      Calling the method multiple times instead of caching in a temp may seem wasteful. In practice, the performance impact is negligible for most code. Profile before optimizing. Refactored code is easier to optimize later because the hot path is isolated.
      
      ---
      
      ## Split Temporary Variable
      
      When a temporary variable is assigned more than once (and it's not a loop counter or collecting variable), it's doing two different jobs. Give each job its own variable.
      
      ### Motivation
      
      A temp assigned twice for different purposes misleads the reader into thinking the assignments are related. Each role deserves its own variable with a descriptive name.
      
      ### Mechanics
      
      1. Rename the first assignment to reflect its purpose
      2. Declare it as `const`/`final` if possible
      3. Find all uses that refer to the first assignment's value and make sure they use the new name
      4. Repeat for each subsequent assignment with a different name
      5. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      let temp = 2 * (height + width);  // perimeter
      console.log(temp);
      temp = height * width;            // area
      console.log(temp);
      ```
      
      **After:**
      ```javascript
      const perimeter = 2 * (height + width);
      console.log(perimeter);
      const area = height * width;
      console.log(area);
      ```
      
      ---
      
      ## Remove Assignments to Parameters
      
      Never assign to a parameter inside a method body. It confuses readers about whether the change is visible to the caller (it isn't in pass-by-value languages; it is in pass-by-reference for object mutations).
      
      ### Mechanics
      
      1. Create a new local variable for the parameter
      2. Replace all assignments to the parameter with assignments to the new variable
      3. Run tests
      
      ### Example
      
      **Before:**
      ```python
      def discount(input_val, quantity):
          if quantity > 50:
              input_val -= 2
          if quantity > 100:
              input_val -= 1
          return input_val
      ```
      
      **After:**
      ```python
      def discount(input_val, quantity):
          result = input_val
          if quantity > 50:
              result -= 2
          if quantity > 100:
              result -= 1
          return result
      ```
      
      ---
      
      ## Replace Method with Method Object
      
      When a method is too tangled with local variables to extract from, move the entire method into its own class where the local variables become fields. Then you can freely extract sub-methods.
      
      ### Motivation
      
      Sometimes a long method has so many interrelated local variables that Extract Method is impossible (too many parameters would be needed). By turning the method into its own class, all locals become fields, accessible to any extracted method without parameters.
      
      ### Mechanics
      
      1. Create a new class named after the method's purpose
      2. Add a field for the original object and for every local variable and parameter
      3. Create a constructor that takes the original object and all parameters
      4. Copy the method body into a `compute()` (or similar) method
      5. Replace the original method with: create the new object, call `compute()`
      6. Now freely extract methods within the new class -- locals are fields, no parameter passing needed
      7. Run tests
      
      ### Example
      
      **Before:**
      ```python
      class Account:
          def gamma(self, input_val, quantity, year_to_date):
              # 50 lines of tangled computation using all three params
              # plus self.fields -- too intertwined to extract
              ...
      ```
      
      **After:**
      ```python
      class GammaCalculation:
          def __init__(self, account, input_val, quantity, year_to_date):
              self.account = account
              self.input_val = input_val
              self.quantity = quantity
              self.year_to_date = year_to_date
      
          def compute(self):
              # Now extract freely -- all variables are fields
              self._apply_quantity_adjustment()
              self._apply_yearly_factor()
              return self.input_val
      
          def _apply_quantity_adjustment(self):
              # can access self.quantity, self.input_val freely
              ...
      
          def _apply_yearly_factor(self):
              ...
      
      class Account:
          def gamma(self, input_val, quantity, year_to_date):
              return GammaCalculation(self, input_val, quantity, year_to_date).compute()
      ```
      
      ---
      
      ## Decision Guide: Which Composing Refactoring to Use
      
      | Situation | Refactoring |
      |-----------|-------------|
      | Code block can be named by intent | Extract Method |
      | Method body is trivial and name adds nothing | Inline Method |
      | Complex expression needs explanation | Extract Variable |
      | Variable adds no meaning beyond the expression | Inline Variable |
      | Same computation needed in multiple methods | Replace Temp with Query |
      | One variable serves two purposes | Split Temporary Variable |
      | Parameter is reassigned inside method | Remove Assignments to Parameters |
      | Long method with too many entangled locals | Replace Method with Method Object |
      
    • moving-features.md 12 KB
      # Moving Features Between Objects
      
      Detailed reference for refactorings that redistribute responsibilities between classes. The fundamental question of object-oriented design is: where should this behavior live? These refactorings provide the mechanical steps to move things to the right place.
      
      ## Table of Contents
      1. [Move Method](#move-method)
      2. [Move Field](#move-field)
      3. [Extract Class](#extract-class)
      4. [Inline Class](#inline-class)
      5. [Hide Delegate](#hide-delegate)
      6. [Remove Middle Man](#remove-middle-man)
      7. [Introduce Foreign Method](#introduce-foreign-method)
      8. [Introduce Local Extension](#introduce-local-extension)
      9. [Decision Guide: Where Does This Behavior Belong?](#decision-guide-where-does-this-behavior-belong)
      
      ---
      
      ## Move Method
      
      Move a method to the class it uses most. A method that accesses more features of another class than its own has Feature Envy and belongs somewhere else.
      
      ### Motivation
      
      The most common reason for moving a method is Feature Envy -- when a method spends most of its time talking to another object. Moving the method reduces coupling: the method now lives where its data lives, so changes to that data don't ripple outward.
      
      ### Mechanics
      
      1. Examine all features (fields and methods) used by the method. Determine which class has the most features used by the method.
      2. Check for related methods in the source class. If other methods also use the same target class, consider moving them together.
      3. Check superclasses and subclasses for overrides or related declarations.
      4. Declare the method in the target class. Copy the body and adjust references -- `this` now refers to the target; the source object may need to be passed as a parameter.
      5. Turn the source method into a delegating method (call the target).
      6. Run tests.
      7. Consider removing the delegating method if no other callers need it.
      8. Run tests.
      
      ### Example
      
      **Before:**
      ```javascript
      class Account {
        overdraftCharge() {
          if (this.type.isPremium()) {
            let result = 10;
            if (this.daysOverdrawn > 7) {
              result += (this.daysOverdrawn - 7) * 0.85;
            }
            return result;
          } else {
            return this.daysOverdrawn * 1.75;
          }
        }
      }
      ```
      
      The method depends heavily on `this.type` (an `AccountType` object). Move it there.
      
      **After:**
      ```javascript
      class AccountType {
        overdraftCharge(daysOverdrawn) {
          if (this.isPremium()) {
            let result = 10;
            if (daysOverdrawn > 7) {
              result += (daysOverdrawn - 7) * 0.85;
            }
            return result;
          } else {
            return daysOverdrawn * 1.75;
          }
        }
      }
      
      class Account {
        overdraftCharge() {
          return this.type.overdraftCharge(this.daysOverdrawn);
        }
      }
      ```
      
      ### Decision Criteria
      
      Move a method when:
      - It uses more fields/methods of another class than its own
      - The target class is likely to change in ways that affect this method
      - Related methods already live in the target class
      
      Don't move when:
      - The method uses features from multiple classes equally (keep it in the most stable location)
      - Polymorphism on the source class is needed
      
      ---
      
      ## Move Field
      
      Move a field to the class that uses it more. Similar to Move Method but for data.
      
      ### Motivation
      
      A field used more by another class signals that the data model is out of alignment with the behavior model. Moving the field keeps data and behavior together.
      
      ### Mechanics
      
      1. If the field is public, encapsulate it first (Encapsulate Field)
      2. Create the field in the target class with a getter and setter
      3. Determine how to reference the target from the source (usually an existing association)
      4. Update the source getter to delegate to the target
      5. Run tests
      6. Remove the field from the source class
      7. Run tests
      
      ### Example
      
      **Before:**
      ```python
      class Customer:
          def __init__(self):
              self.discount_rate = 0.0
      
      class Order:
          def discounted_total(self):
              return self.base_total() - (self.base_total() * self.customer.discount_rate)
      ```
      
      `discount_rate` is only read by `Order` through `Customer`. If most logic involving `discount_rate` lives in the customer's pricing context, keep it in `Customer`. But if `Order` is the primary consumer and `discount_rate` is really about order pricing policy, consider moving it.
      
      ---
      
      ## Extract Class
      
      Split a class that does two things into two classes that each do one thing.
      
      ### Motivation
      
      A class with too many responsibilities grows too large and becomes hard to understand. If you can identify a coherent subset of fields and methods that relate to each other more than to the rest of the class, that subset deserves its own class.
      
      ### Mechanics
      
      1. Identify the subset of responsibilities to split out
      2. Create a new class named after the split-out responsibility
      3. Add a link from the old class to the new class
      4. Use Move Field for each field in the subset
      5. Use Move Method for each method in the subset
      6. Review the interfaces of both classes. Remove unneeded methods, rename as appropriate.
      7. Decide whether to expose the new class or hide it behind the original
      8. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      class Person {
        constructor() {
          this.name = '';
          this.officeAreaCode = '';
          this.officeNumber = '';
        }
      
        get telephoneNumber() {
          return `(${this.officeAreaCode}) ${this.officeNumber}`;
        }
      }
      ```
      
      **After:**
      ```javascript
      class TelephoneNumber {
        constructor() {
          this.areaCode = '';
          this.number = '';
        }
      
        toString() {
          return `(${this.areaCode}) ${this.number}`;
        }
      }
      
      class Person {
        constructor() {
          this.name = '';
          this.telephoneNumber = new TelephoneNumber();
        }
      
        get telephone() {
          return this.telephoneNumber.toString();
        }
      }
      ```
      
      ### Signals That Suggest Extraction
      
      | Signal | What to Extract |
      |--------|----------------|
      | Field name prefix groups (e.g., `shippingStreet`, `shippingCity`) | `ShippingAddress` class |
      | Methods that only use a subset of fields | The subset + its methods = new class |
      | Subsets change at different rates | The faster-changing subset deserves its own class |
      | Subsets have different collaborators | Each collaborator relationship = potential class boundary |
      
      ---
      
      ## Inline Class
      
      The inverse of Extract Class. Merge a class that no longer carries its weight back into another class.
      
      ### Motivation
      
      A class that does too little -- perhaps after previous refactorings moved its responsibilities elsewhere -- adds complexity without value. Fold it back into the class that uses it.
      
      ### Mechanics
      
      1. For each public method and field of the source class, create a corresponding member in the target class
      2. Change all references to the source class to use the target class instead
      3. Run tests
      4. Delete the source class
      5. Run tests
      
      ### When to Use
      
      - The class has only one or two trivial methods
      - The class was created by Extract Class but subsequent refactorings emptied it
      - The class adds indirection without any logic, validation, or behavior of its own
      
      ---
      
      ## Hide Delegate
      
      Encapsulate the fact that one object delegates to another. Create a method on the server that hides the delegate from the client, enforcing the Law of Demeter.
      
      ### Motivation
      
      When a client calls `person.getDepartment().getManager()`, the client knows about the `Department` class -- it's coupled to the navigation structure. If `Department` changes its interface, the client breaks. By adding `person.getManager()` (which internally calls `department.getManager()`), the client only knows about `Person`.
      
      ### Mechanics
      
      1. For each method the client calls on the delegate, create a simple delegating method on the server
      2. Change the client to call the server method instead
      3. If no client needs the delegate accessor anymore, remove it
      4. Run tests
      
      ### Example
      
      **Before:**
      ```python
      # Client code:
      manager = person.department.manager
      ```
      
      **After:**
      ```python
      class Person:
          @property
          def manager(self):
              return self.department.manager
      
      # Client code:
      manager = person.manager
      ```
      
      ### The Trade-Off
      
      Hiding every delegate leads to the Middle Man smell -- a class that does nothing but forward calls. The right balance:
      
      | Situation | Action |
      |-----------|--------|
      | Delegate's interface is unstable | Hide it (protect callers from change) |
      | Client uses many delegate methods | Consider Hide Delegate for each |
      | Server is becoming pure forwarding | Remove Middle Man |
      | Chain is deep (a.b.c.d) | Definitely hide |
      
      ---
      
      ## Remove Middle Man
      
      The inverse of Hide Delegate. When a class consists primarily of methods that delegate to another class, let the client call the delegate directly.
      
      ### Motivation
      
      As a system evolves, more and more delegating methods accumulate until the "server" class adds no value -- it's just a pass-through. At that point, remove the indirection.
      
      ### Mechanics
      
      1. Create a getter for the delegate on the server (if one doesn't exist)
      2. For each delegating method that adds no value, redirect the client to call the delegate directly
      3. Remove the delegating method from the server
      4. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      class Person {
        get manager() { return this.department.manager; }
        get budget() { return this.department.budget; }
        get headcount() { return this.department.headcount; }
        get location() { return this.department.location; }
        // ... 10 more forwarding methods
      }
      ```
      
      **After:**
      ```javascript
      class Person {
        get department() { return this._department; }
      }
      
      // Client:
      const manager = person.department.manager;
      ```
      
      ---
      
      ## Introduce Foreign Method
      
      When a server class needs an additional method but you can't modify it (third-party library, frozen module), create the method in the client class and pass the server object as the first argument.
      
      ### Motivation
      
      A utility method that "should" be on the server class but can't be added there. The foreign method is a workaround -- mark it as such, so if the server class is ever opened for modification, the method can be moved.
      
      ### Example
      
      ```python
      # Server class (third-party, can't modify):
      # date = Date(year, month, day)
      
      # Foreign method in client:
      def next_day(date):
          """Foreign method -- should be on Date class."""
          return Date(date.year, date.month, date.day + 1)
      ```
      
      ---
      
      ## Introduce Local Extension
      
      When you need several foreign methods on a server class you can't modify, create a new class -- either a subclass or a wrapper -- that adds the missing methods.
      
      ### Subclass vs. Wrapper
      
      | Approach | When to Use |
      |----------|-------------|
      | Subclass | When you can subclass the server; simplest approach |
      | Wrapper (Decorator) | When you can't subclass (final class); forward all original methods |
      
      ### Example (Wrapper)
      
      ```javascript
      class EnhancedDate {
        constructor(date) {
          this._original = date;
        }
      
        // Forward original methods
        getYear() { return this._original.getYear(); }
        getMonth() { return this._original.getMonth(); }
      
        // New methods
        nextDay() {
          return new EnhancedDate(
            new Date(this._original.getTime() + 86400000)
          );
        }
      
        isWeekend() {
          const day = this._original.getDay();
          return day === 0 || day === 6;
        }
      }
      ```
      
      ---
      
      ## Decision Guide: Where Does This Behavior Belong?
      
      Use these questions to decide whether and where to move code:
      
      | Question | If Yes | Action |
      |----------|--------|--------|
      | Does this method use more of another class's features? | Feature Envy | Move Method to that class |
      | Is this field used more by another class? | Misplaced data | Move Field to that class |
      | Does this class have two groups of fields that don't interact? | Multiple responsibilities | Extract Class |
      | Is this class just a thin wrapper with no logic? | Unnecessary indirection | Inline Class |
      | Is the client navigating through an object chain? | Tight coupling | Hide Delegate |
      | Is this class just forwarding calls? | Middle Man smell | Remove Middle Man |
      | Need to add a method to a class you can't modify? | Missing feature | Introduce Foreign Method or Local Extension |
      
      ### The Responsibility Placement Heuristic
      
      When unsure where to put a method, ask: **"If the data this method uses changes, which class should need to be updated?"** The method belongs in that class. This keeps data and behavior together, minimizing the ripple effect of change.
      
    • organizing-data.md 12.7 KB
      # Organizing Data
      
      Detailed reference for refactorings that improve how data is represented. Raw primitives, magic numbers, exposed fields, and mutable collections create subtle bugs and scatter domain knowledge. These refactorings replace primitive representations with objects that encapsulate behavior and enforce invariants.
      
      ## Table of Contents
      1. [Replace Data Value with Object](#replace-data-value-with-object)
      2. [Change Value to Reference](#change-value-to-reference)
      3. [Replace Array with Object](#replace-array-with-object)
      4. [Replace Magic Number with Symbolic Constant](#replace-magic-number-with-symbolic-constant)
      5. [Encapsulate Field](#encapsulate-field)
      6. [Encapsulate Collection](#encapsulate-collection)
      7. [Replace Type Code with Class](#replace-type-code-with-class)
      8. [Decision Guide: Which Data Refactoring to Use](#decision-guide-which-data-refactoring-to-use)
      
      ---
      
      ## Replace Data Value with Object
      
      Wrap a primitive data item in a class when it has behavior or validation associated with it. This is the cure for Primitive Obsession.
      
      ### Motivation
      
      A data value starts life as a simple string or number. Then you add validation. Then formatting. Then comparison logic. Then the same validation appears in three places. At that point, the value deserves to be an object.
      
      ### Mechanics
      
      1. Create a class for the value with a constructor that takes the primitive
      2. Add validation in the constructor
      3. Add any behavior methods (formatting, comparison, etc.)
      4. Change the field type from primitive to the new class
      5. Update all code that sets the field to create an instance of the new class
      6. Update all code that reads the field to use the object's methods
      7. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      class Order {
        constructor(customer) {
          this.customer = customer; // just a string name
        }
      }
      
      // Scattered validation in multiple places:
      if (order.customer === '') throw new Error('no customer');
      if (otherOrder.customer === '') throw new Error('no customer');
      ```
      
      **After:**
      ```javascript
      class Customer {
        constructor(name) {
          if (!name || name.trim() === '') {
            throw new Error('Customer name is required');
          }
          this._name = name.trim();
        }
      
        get name() { return this._name; }
      
        equals(other) {
          return other instanceof Customer && this._name === other._name;
        }
      }
      
      class Order {
        constructor(customer) {
          this.customer = new Customer(customer);
        }
      }
      ```
      
      ### Common Primitive-to-Object Upgrades
      
      | Primitive | Object | Behavior It Gains |
      |-----------|--------|-------------------|
      | `String email` | `EmailAddress` | Format validation, domain extraction |
      | `number cents` | `Money` | Currency, rounding rules, arithmetic |
      | `String phone` | `PhoneNumber` | Formatting, country code parsing |
      | `number lat, number lng` | `Coordinates` | Distance calculation, validation |
      | `String startDate, String endDate` | `DateRange` | Contains, overlaps, duration |
      | `number celsius` | `Temperature` | Unit conversion, comparison |
      | `String hex` | `Color` | Parsing, lightness, contrast |
      | `number status` | `OrderStatus` | Valid transitions, display name |
      
      ---
      
      ## Change Value to Reference
      
      Convert a value object into a reference object when you need identity semantics -- when changes to one instance should be visible everywhere that instance is used.
      
      ### Motivation
      
      When you have multiple copies of the same customer, changing the phone number on one doesn't change it on the others. If business rules require a single shared instance, convert value to reference using a registry or repository.
      
      ### Mechanics
      
      1. Determine or create a factory method for the object
      2. Set up a registry (map, repository, or lookup service) to store instances
      3. Change the factory to check the registry before creating new instances
      4. Change client code to use the factory instead of the constructor
      5. Run tests
      
      ### Example
      
      ```javascript
      // Registry pattern:
      class CustomerRepository {
        constructor() {
          this._customers = new Map();
        }
      
        get(id) {
          if (!this._customers.has(id)) {
            this._customers.set(id, new Customer(id));
          }
          return this._customers.get(id);
        }
      }
      
      // All orders for customer #123 now share the same Customer object
      const repo = new CustomerRepository();
      const order1 = new Order(repo.get(123));
      const order2 = new Order(repo.get(123));
      // order1.customer === order2.customer  // true (same reference)
      ```
      
      ### Value vs. Reference: Decision Guide
      
      | Question | Value | Reference |
      |----------|-------|-----------|
      | Do you need identity (same object everywhere)? | No | Yes |
      | Is the object immutable? | Typically | May be mutable |
      | Do you compare by content? | Yes (`equals()`) | No (identity `===`) |
      | Examples | Money, DateRange, Color | Customer, Account, Product |
      
      ---
      
      ## Replace Array with Object
      
      Replace an array used as a record (where each position has a different meaning) with an object with named fields.
      
      ### Motivation
      
      `row[0]` is the name, `row[1]` is the age, `row[2]` is the department. This is fragile, unreadable, and type-unsafe. Named fields make the structure self-documenting.
      
      ### Mechanics
      
      1. Create a class with a field for each array position
      2. Add getters and setters for each field
      3. Replace array creation with object construction
      4. Replace positional access with named access
      5. Run tests
      
      ### Example
      
      **Before:**
      ```python
      performance = ["Liverpool", 15, 2]
      name = performance[0]
      wins = performance[1]
      losses = performance[2]
      ```
      
      **After:**
      ```python
      class Performance:
          def __init__(self, name, wins, losses):
              self.name = name
              self.wins = wins
              self.losses = losses
      
      performance = Performance("Liverpool", 15, 2)
      name = performance.name
      wins = performance.wins
      losses = performance.losses
      ```
      
      ---
      
      ## Replace Magic Number with Symbolic Constant
      
      Replace a literal number that has a particular meaning with a named constant.
      
      ### Motivation
      
      `9.81` means nothing in code. `GRAVITATIONAL_ACCELERATION = 9.81` communicates intent, prevents typos (the constant name is checked by the compiler), and centralizes the value for easy change.
      
      ### Mechanics
      
      1. Declare a constant and set it to the magic number
      2. Find all occurrences of the magic number
      3. Replace each occurrence with the constant (check that each occurrence represents the same concept -- the number `100` might mean "percentage" in one place and "max items" in another)
      4. Run tests
      
      ### Common Magic Number Categories
      
      | Category | Before | After |
      |----------|--------|-------|
      | Physics | `9.81` | `GRAVITATIONAL_ACCELERATION` |
      | Business rules | `0.08` | `SALES_TAX_RATE` |
      | Limits | `255` | `MAX_RGB_VALUE` |
      | HTTP | `404` | `HTTP_NOT_FOUND` |
      | Time | `86400` | `SECONDS_PER_DAY` |
      | Retry | `3` | `MAX_RETRY_ATTEMPTS` |
      | Thresholds | `100` | `FREE_SHIPPING_THRESHOLD` |
      
      ### When NOT to Replace
      
      - `0` and `1` in arithmetic are usually fine as literals
      - Loop counters (`for i in range(10)`) are obvious from context
      - Array index `[0]` for "first element" is idiomatic
      
      ---
      
      ## Encapsulate Field
      
      Replace direct access to a public field with getter and setter methods.
      
      ### Motivation
      
      A public field gives you no control over reads and writes. You can't add validation, logging, lazy initialization, or computed values later without changing every caller. Encapsulation creates a seam for future change.
      
      ### Mechanics
      
      1. Create getter and setter methods for the field
      2. Find all references to the field and replace reads with the getter, writes with the setter
      3. Make the field private
      4. Run tests
      
      ### Example
      
      **Before:**
      ```python
      class Person:
          def __init__(self, name):
              self.name = name  # public field
      
      # Client:
      person.name = "   Bob   "  # no validation, no trimming
      ```
      
      **After:**
      ```python
      class Person:
          def __init__(self, name):
              self._name = None
              self.name = name  # uses the setter
      
          @property
          def name(self):
              return self._name
      
          @name.setter
          def name(self, value):
              if not value or not value.strip():
                  raise ValueError("Name cannot be empty")
              self._name = value.strip()
      ```
      
      ---
      
      ## Encapsulate Collection
      
      Don't return a raw mutable collection from a getter. Instead, return an unmodifiable view or a copy, and provide explicit add/remove methods.
      
      ### Motivation
      
      When a getter returns a mutable list, callers can add, remove, or clear items without the owning object knowing. This breaks encapsulation -- the object can't enforce invariants, fire events, or validate changes.
      
      ### Mechanics
      
      1. Add `addItem()` and `removeItem()` methods on the owning class
      2. Change the getter to return an unmodifiable view (or a copy)
      3. Find all callers that mutate the collection through the getter and change them to use the add/remove methods
      4. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      class Course {}
      
      class Person {
        get courses() { return this._courses; }
        set courses(list) { this._courses = list; }
      }
      
      // Client can mutate freely:
      person.courses.push(newCourse);        // bypasses Person
      person.courses.splice(0, 1);           // bypasses Person
      person.courses = [];                   // replaces internal state
      ```
      
      **After:**
      ```javascript
      class Person {
        get courses() {
          return [...this._courses]; // return a copy
        }
      
        addCourse(course) {
          this._courses.push(course);
        }
      
        removeCourse(course) {
          const index = this._courses.indexOf(course);
          if (index === -1) throw new RangeError('Course not found');
          this._courses.splice(index, 1);
        }
      
        get numberOfCourses() {
          return this._courses.length;
        }
      }
      ```
      
      ### Language-Specific Patterns
      
      | Language | Unmodifiable Return |
      |----------|-------------------|
      | Java | `Collections.unmodifiableList(list)` |
      | JavaScript | `[...this._items]` or `Object.freeze([...this._items])` |
      | Python | `tuple(self._items)` or `list(self._items)` (return a copy) |
      | C# | `items.AsReadOnly()` |
      | Go | Return a slice copy: `append([]T{}, items...)` |
      
      ---
      
      ## Replace Type Code with Class
      
      Replace a type code (integer or string constant) that does not affect behavior with a proper class. Use when the type code is used for categorization but doesn't drive conditional logic.
      
      ### When to Use Which
      
      | Situation | Refactoring |
      |-----------|-------------|
      | Type code is informational only (no behavior change) | Replace Type Code with Class |
      | Type code drives behavior via conditionals | Replace Type Code with Subclasses |
      | Type code can change at runtime | Replace Type Code with Strategy/State |
      | Type code has few values and language supports it | Use an Enum |
      
      ### Replace Type Code with Subclasses
      
      Used when the type code determines behavior through conditionals.
      
      **Before:**
      ```javascript
      class Employee {
        constructor(type) {
          this._type = type; // 'engineer', 'manager', 'salesperson'
        }
      
        calculatePay() {
          switch (this._type) {
            case 'engineer': return this.basePay;
            case 'manager': return this.basePay + this.bonus;
            case 'salesperson': return this.basePay + this.commission;
          }
        }
      
        canApproveExpenses() {
          return this._type === 'manager';
        }
      }
      ```
      
      **After:**
      ```javascript
      class Employee {
        calculatePay() { throw new Error('abstract'); }
        canApproveExpenses() { return false; }
      }
      
      class Engineer extends Employee {
        calculatePay() { return this.basePay; }
      }
      
      class Manager extends Employee {
        calculatePay() { return this.basePay + this.bonus; }
        canApproveExpenses() { return true; }
      }
      
      class Salesperson extends Employee {
        calculatePay() { return this.basePay + this.commission; }
      }
      ```
      
      ### Replace Type Code with Strategy/State
      
      Used when the type code can change at runtime (an employee can be promoted from engineer to manager), so subclassing the employee itself is not possible.
      
      **After (Strategy):**
      ```javascript
      class Employee {
        constructor(type) {
          this._type = type; // EmployeeType strategy object
        }
      
        calculatePay() {
          return this._type.calculatePay(this);
        }
      
        promoteToManager() {
          this._type = new ManagerType();
        }
      }
      
      class EngineerType {
        calculatePay(employee) { return employee.basePay; }
      }
      
      class ManagerType {
        calculatePay(employee) { return employee.basePay + employee.bonus; }
      }
      ```
      
      ---
      
      ## Decision Guide: Which Data Refactoring to Use
      
      | Situation | Refactoring |
      |-----------|-------------|
      | Primitive value has associated behavior | Replace Data Value with Object |
      | Need one shared instance across the system | Change Value to Reference |
      | Array positions have different meanings | Replace Array with Object |
      | Literal number has domain meaning | Replace Magic Number with Symbolic Constant |
      | Public field needs future flexibility | Encapsulate Field |
      | Getter returns mutable collection | Encapsulate Collection |
      | Type code is informational | Replace Type Code with Class / Enum |
      | Type code drives behavior | Replace Type Code with Subclasses |
      | Type code changes at runtime | Replace Type Code with Strategy |
      
    • refactoring-workflow.md 14 KB
      # Refactoring Workflow
      
      Detailed reference for when and how to refactor safely. The discipline of refactoring is as important as knowing the individual transformations. This reference covers the refactoring cycle, timing, safety techniques, and strategies for large-scale refactoring in production systems.
      
      ## Table of Contents
      1. [The Refactoring Cycle](#the-refactoring-cycle)
      2. [When to Refactor](#when-to-refactor)
      3. [When NOT to Refactor](#when-not-to-refactor)
      4. [Testing and Refactoring](#testing-and-refactoring)
      5. [Refactoring and Performance](#refactoring-and-performance)
      6. [Branch by Abstraction](#branch-by-abstraction)
      7. [Parallel Change (Expand-Migrate-Contract)](#parallel-change-expand-migrate-contract)
      8. [Large-Scale Refactoring Strategies](#large-scale-refactoring-strategies)
      9. [Refactoring Checklist](#refactoring-checklist)
      
      ---
      
      ## The Refactoring Cycle
      
      Every refactoring follows the same four-step loop:
      
      ```
      1. Run tests → GREEN
      2. Apply one small structural change
      3. Run tests → GREEN
      4. Commit
      ```
      
      **Then repeat.**
      
      ### Why Small Steps Matter
      
      | Approach | Risk | Recovery Time |
      |----------|------|---------------|
      | One refactoring at a time | Minimal -- if tests fail, the cause is obvious | Seconds (revert one change) |
      | Several refactorings between tests | Medium -- must debug to find which one broke | Minutes |
      | Big-bang rewrite | Maximum -- structural and behavioral changes mixed | Hours to days (or never) |
      
      **Rule:** If a test fails after a refactoring step, **revert immediately**. Don't debug. The step was too big or wrong. Revert, think, try a smaller step.
      
      ### The Two Hats
      
      Martin Fowler describes two distinct modes of work. You wear only one "hat" at a time:
      
      | Hat | What You Do | What You Don't Do |
      |-----|-------------|-------------------|
      | **Refactoring** | Change structure, keep behavior identical | Add features, fix bugs, change tests |
      | **Adding Function** | Add new behavior, write new tests | Change existing code structure |
      
      **Switching hats:** You may alternate frequently, but never wear both at once. A typical sequence:
      
      1. Refactoring hat: restructure to make the new feature easy to add. Commit.
      2. Adding-function hat: add the feature and its tests. Commit.
      3. Refactoring hat: clean up any mess the new feature introduced. Commit.
      
      ---
      
      ## When to Refactor
      
      ### Preparatory Refactoring (Refactor to Make the Change Easy)
      
      **Trigger:** You're about to add a feature, and the code isn't structured to accommodate it easily.
      
      **Example:** You need to add a new payment method. The payment logic is in a long if/else chain. Before adding the new branch, refactor to Replace Conditional with Polymorphism. Now adding the new payment method means creating one new class.
      
      **Kent Beck's quote:** "Make the change easy (warning: this may be hard), then make the easy change."
      
      **The payoff:** The feature is faster to add, less likely to contain bugs, and the refactoring improves the code for all future changes, not just this one.
      
      ### Comprehension Refactoring (Refactor to Understand)
      
      **Trigger:** You're reading code and struggling to understand it. Rename variables, extract methods, and reorganize to make the code express its intent.
      
      **Example:** You encounter a function called `calc` with variables named `a`, `b`, and `temp`. As you figure out what each does, rename them: `calculateMonthlyPayment`, `principal`, `interestRate`, `monthlyAmount`. The understanding you gain is encoded in the code itself.
      
      **Ward Cunningham's insight:** "By refactoring, I move the understanding from my head into the code itself."
      
      ### Litter-Pickup Refactoring (Boy Scout Rule)
      
      **Trigger:** You touch a file for any reason and notice a small improvement you can make. Do it.
      
      **Examples:**
      - Rename a misleading variable name
      - Extract a method from a long function
      - Remove dead code
      - Add a missing guard clause
      
      **The rule:** Leave the code cleaner than you found it. Each small improvement compounds over time. A codebase that is consistently cleaned by every developer who touches it stays healthy.
      
      ### Rule of Three
      
      **Trigger:** The third time you see duplicated code or a repeated pattern.
      
      **The progression:**
      1. First time: Write it
      2. Second time: Wince at the duplication but tolerate it
      3. Third time: Refactor -- extract the common pattern
      
      **Why three, not two:** Premature abstraction is as dangerous as duplication. Two occurrences might be coincidental. Three confirms the pattern.
      
      ### Long-Term Refactoring
      
      **Trigger:** A large structural problem that can't be fixed in one session.
      
      **Examples:**
      - Replacing a library or framework
      - Splitting a monolith into modules
      - Changing a pervasive data representation
      
      **Approach:** The team agrees on a target architecture. Everyone makes small changes toward it during regular work. No one stops feature development for a "refactoring sprint."
      
      ---
      
      ## When NOT to Refactor
      
      Not every piece of code deserves refactoring. Save your effort for code that justifies it.
      
      ### Code You Should Leave Alone
      
      | Situation | Why |
      |-----------|-----|
      | The code works and nobody needs to modify it | If it's behind a clean interface, its internal messiness costs nothing |
      | It's easier to rewrite from scratch | If the code is small and the rewrite is straightforward, don't polish what you'll replace |
      | There are no tests and adding them is impractical | Refactoring without tests is too risky; consider characterization tests first |
      | The code will be deleted soon | Don't beautify code with a known end-of-life |
      | You're exploring or prototyping | Throwaway code benefits from speed, not structure |
      
      ### The "Messy Middle" Trap
      
      Some teams swing between two extremes:
      - **Never refactor:** Technical debt accumulates until development grinds to a halt
      - **Always refactor:** Gold-plating code that doesn't need it, shipping features slowly
      
      The right balance: **Refactor code that you're about to change, or code that's actively hurting velocity.** Don't refactor code just because it's not beautiful.
      
      ---
      
      ## Testing and Refactoring
      
      ### The Safety Net
      
      Tests are not optional for refactoring. Without them, you cannot verify that behavior is preserved.
      
      | Test Type | Role in Refactoring |
      |-----------|-------------------|
      | Unit tests | Fast feedback on individual method behavior |
      | Integration tests | Verify behavior across collaborating objects |
      | Characterization tests | Capture existing behavior of legacy code (the starting point) |
      | Regression tests | Ensure the entire system still works after changes |
      
      ### Characterization Tests
      
      When you encounter code without tests that you need to refactor:
      
      1. Run the code with known inputs
      2. Observe the actual outputs (even if you think they're "wrong")
      3. Write tests that assert the actual current behavior
      4. Now you have a safety net -- refactor freely
      
      **Example:**
      ```python
      def test_weird_edge_case():
          # This behavior may be "wrong" but it's what exists.
          # Capture it so refactoring doesn't accidentally change it.
          result = calculate_shipping(weight=0, distance=100)
          assert result == 5.99  # Captures existing behavior
      ```
      
      ### Test-Driven Refactoring Steps
      
      1. **Before starting:** Run all tests. If any fail, fix them first.
      2. **After each refactoring step:** Run tests. All must pass.
      3. **If a test fails:** Revert immediately. Don't debug.
      4. **After completing a logical group of refactorings:** Commit.
      5. **If you discover a bug during refactoring:** Stop refactoring. Fix the bug (adding-function hat). Then resume refactoring.
      
      ---
      
      ## Refactoring and Performance
      
      ### The Common Fear
      
      "Won't all these small methods and indirection make the code slower?"
      
      ### The Reality
      
      1. Most performance concerns about refactored code are unfounded. Modern compilers and runtimes inline small methods.
      2. Performance bottlenecks are almost never where you think they are. Profile first.
      3. Well-structured code is **easier** to optimize because the hot path is isolated.
      
      ### The Three-Step Performance Strategy
      
      1. **Write clear code first.** Don't optimize during refactoring.
      2. **Profile the running system.** Find the actual bottleneck (usually 10% of the code causes 90% of the performance issue).
      3. **Optimize only the measured hot path.** Well-refactored code makes this easy because the hot path is in a small, isolated method.
      
      ### When Refactoring Genuinely Hurts Performance
      
      | Refactoring | Potential Cost | Mitigation |
      |-------------|---------------|------------|
      | Replace Temp with Query | Method called multiple times instead of cached once | Cache if profiling shows impact |
      | Extract Method | Additional method call overhead | Usually inlined by the compiler/JIT |
      | Replace Conditional with Polymorphism | Virtual dispatch instead of branch | Negligible in most cases; profile if in doubt |
      | Introduce Parameter Object | Object allocation for each call | Often optimized away; pool if necessary |
      
      **Key insight:** Optimization and refactoring are separate concerns. Refactor first for clarity, then optimize the measured bottleneck.
      
      ---
      
      ## Branch by Abstraction
      
      A technique for making large-scale changes to a widely-used component without creating a long-lived feature branch.
      
      ### When to Use
      
      - Replacing a framework, library, or major internal component
      - The replacement will take weeks or months
      - You need to keep shipping features during the transition
      - Feature branches would become stale and cause merge conflicts
      
      ### How It Works
      
      ```
      Step 1: Identify the component to replace (OldComponent)
      Step 2: Create an abstraction layer (interface) that wraps OldComponent
      Step 3: Change all callers to use the abstraction (deploy incrementally)
      Step 4: Create NewComponent that implements the same abstraction
      Step 5: Switch the abstraction to point to NewComponent (one change, deploy)
      Step 6: Remove OldComponent and the abstraction layer (clean up)
      ```
      
      ### Example
      
      **Step 1-2:** Introduce the abstraction
      ```python
      # Before: callers use OldPaymentGateway directly
      class OldPaymentGateway:
          def charge(self, amount, card): ...
      
      # After: introduce abstraction
      class PaymentGateway(ABC):
          @abstractmethod
          def charge(self, amount, card): ...
      
      class OldPaymentGateway(PaymentGateway):
          def charge(self, amount, card): ...  # existing implementation
      ```
      
      **Step 3:** Migrate callers to use `PaymentGateway` (the abstraction). Deploy.
      
      **Step 4:** Build `NewPaymentGateway(PaymentGateway)`. Test thoroughly.
      
      **Step 5:** Switch the wiring:
      ```python
      # In configuration:
      # gateway = OldPaymentGateway()  # old
      gateway = NewPaymentGateway()    # new
      ```
      
      **Step 6:** Delete `OldPaymentGateway`. Optionally inline the abstraction if only one implementation remains.
      
      ---
      
      ## Parallel Change (Expand-Migrate-Contract)
      
      A technique for making breaking API changes safely by running old and new versions side by side.
      
      ### When to Use
      
      - Renaming a widely-used method or changing its signature
      - Changing a data format while consumers still read the old format
      - Migrating from one API to another when you can't update all consumers at once
      
      ### The Three Phases
      
      **1. Expand:** Add the new version alongside the old one.
      ```python
      class User:
          def get_full_name(self):     # new name
              return f"{self.first} {self.last}"
      
          def getFullName(self):       # old name, still works
              return self.get_full_name()  # delegates to new
      ```
      
      **2. Migrate:** Update all callers to use the new version. This can happen incrementally across multiple deployments.
      
      **3. Contract:** Remove the old version once all callers have migrated.
      ```python
      class User:
          def get_full_name(self):     # only the new version remains
              return f"{self.first} {self.last}"
      ```
      
      ### Parallel Change for Data
      
      ```
      1. Expand: Write to both old and new columns/formats
      2. Migrate: Update all readers to use the new format
      3. Contract: Stop writing the old format, remove old column
      ```
      
      ---
      
      ## Large-Scale Refactoring Strategies
      
      ### The Strangler Fig Pattern
      
      Gradually replace a legacy system by building new functionality around it, routing more and more traffic to the new system until the old one can be decommissioned.
      
      | Phase | Action |
      |-------|--------|
      | 1. Intercept | Place a routing layer in front of the legacy system |
      | 2. Build new | Implement new components behind the router |
      | 3. Redirect | Route requests to new components as they're ready |
      | 4. Retire | Decommission old components once no traffic reaches them |
      
      ### Mikado Method
      
      For complex refactorings with many interdependencies:
      
      1. Try the refactoring you want to make
      2. If it breaks, note what needs to change first (the prerequisites)
      3. Revert your change
      4. Recursively fix the prerequisites (each may have its own prerequisites)
      5. Build a dependency graph (the "Mikado Graph")
      6. Solve the graph from the leaves (no-dependency tasks) toward the root
      
      ### Feature Toggles During Refactoring
      
      Use feature flags to gradually roll out a refactored component:
      
      ```python
      if feature_flag('new_pricing_engine'):
          return new_pricing_engine.calculate(order)
      else:
          return old_pricing_engine.calculate(order)
      ```
      
      This allows:
      - Incremental rollout (10% of traffic, then 50%, then 100%)
      - Instant rollback by toggling the flag
      - A/B comparison of old vs. new behavior
      
      ---
      
      ## Refactoring Checklist
      
      Use this checklist before, during, and after refactoring sessions:
      
      ### Before Starting
      
      - [ ] All existing tests pass (green)
      - [ ] You've identified the specific smell or improvement target
      - [ ] You can name the refactoring(s) you'll apply
      - [ ] The code has test coverage for the area you'll change (add characterization tests if not)
      
      ### During Refactoring
      
      - [ ] Each step is the smallest possible transformation
      - [ ] Tests run after every step
      - [ ] You revert immediately if tests fail (don't debug)
      - [ ] You're wearing only the refactoring hat (no new features)
      - [ ] You commit after each logical group of steps
      
      ### After Completing
      
      - [ ] All tests still pass
      - [ ] The code is easier to read than before
      - [ ] Variable and method names reveal intent
      - [ ] No unnecessary comments remain (the code explains itself)
      - [ ] No new smells were introduced
      - [ ] Changes are committed with a clear message describing the refactoring
      
    • simplifying-conditionals.md 12.8 KB
      # Simplifying Conditional Logic
      
      Detailed reference for refactorings that tame complex conditional structures. Conditionals are the hardest code to read and the most likely to harbor bugs. These refactorings decompose, consolidate, and replace conditionals with clearer alternatives.
      
      ## Table of Contents
      1. [Decompose Conditional](#decompose-conditional)
      2. [Consolidate Conditional Expression](#consolidate-conditional-expression)
      3. [Replace Nested Conditional with Guard Clauses](#replace-nested-conditional-with-guard-clauses)
      4. [Replace Conditional with Polymorphism](#replace-conditional-with-polymorphism)
      5. [Introduce Special Case (Null Object)](#introduce-special-case-null-object)
      6. [Introduce Assertion](#introduce-assertion)
      7. [Decision Guide: Which Conditional Refactoring to Use](#decision-guide-which-conditional-refactoring-to-use)
      
      ---
      
      ## Decompose Conditional
      
      Extract the condition, the then-branch, and the else-branch of a complex conditional into well-named methods.
      
      ### Motivation
      
      A long `if` statement with a compound condition and multi-line branches forces the reader to simulate every path mentally. By naming each part, you turn the conditional into readable prose.
      
      ### Mechanics
      
      1. Extract the condition into a method whose name describes the meaning (not the mechanics)
      2. Extract the then-body into a method whose name describes what happens
      3. Extract the else-body into a method whose name describes what happens
      4. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      function calculateCharge(date, quantity, plan) {
        let charge;
        if (date.getMonth() >= 6 && date.getMonth() <= 8) {
          charge = quantity * plan.summerRate;
        } else {
          charge = quantity * plan.regularRate + plan.regularServiceCharge;
        }
        return charge;
      }
      ```
      
      **After:**
      ```javascript
      function calculateCharge(date, quantity, plan) {
        if (isSummer(date)) {
          return summerCharge(quantity, plan);
        } else {
          return regularCharge(quantity, plan);
        }
      }
      
      function isSummer(date) {
        return date.getMonth() >= 6 && date.getMonth() <= 8;
      }
      
      function summerCharge(quantity, plan) {
        return quantity * plan.summerRate;
      }
      
      function regularCharge(quantity, plan) {
        return quantity * plan.regularRate + plan.regularServiceCharge;
      }
      ```
      
      ### Naming the Condition
      
      | Condition Expression | Good Name |
      |---------------------|-----------|
      | `date.getMonth() >= 6 && date.getMonth() <= 8` | `isSummer(date)` |
      | `user.age >= 18 && user.hasConsent` | `isEligible(user)` |
      | `cart.total > 100 && !cart.hasPromo` | `qualifiesForDiscount(cart)` |
      | `retries < MAX && !response.ok` | `shouldRetry(retries, response)` |
      | `file.size > 0 && file.ext === '.csv'` | `isValidUpload(file)` |
      
      The condition name should answer a yes/no question using the domain vocabulary.
      
      ---
      
      ## Consolidate Conditional Expression
      
      Combine a series of conditional checks that all lead to the same result into a single conditional with a descriptive name.
      
      ### Motivation
      
      When multiple conditions return the same value, combining them into one named check makes the logic clearer: "All of these mean the same thing -- this situation is X."
      
      ### Mechanics
      
      1. Verify that all the conditionals have no side effects
      2. Combine using logical operators (`&&`, `||`)
      3. Extract the combined condition into a named method
      4. Run tests
      
      ### Example
      
      **Before:**
      ```python
      def disability_amount(employee):
          if employee.seniority < 2:
              return 0
          if employee.months_disabled > 12:
              return 0
          if employee.is_part_time:
              return 0
          # compute disability amount...
          return base_amount * 1.5
      ```
      
      **After:**
      ```python
      def disability_amount(employee):
          if is_not_eligible_for_disability(employee):
              return 0
          return base_amount * 1.5
      
      def is_not_eligible_for_disability(employee):
          return (employee.seniority < 2
                  or employee.months_disabled > 12
                  or employee.is_part_time)
      ```
      
      ### When to Consolidate vs. Keep Separate
      
      | Situation | Action |
      |-----------|--------|
      | All conditions mean the same business concept | Consolidate into one named check |
      | Conditions are independent with different reasons | Keep separate (each deserves its own name) |
      | Conditions should be evaluated in sequence for performance | Keep separate for short-circuit clarity |
      
      ---
      
      ## Replace Nested Conditional with Guard Clauses
      
      Handle special cases and edge conditions at the top of the method and return early, leaving the main path of execution flat and unindented.
      
      ### Motivation
      
      Deeply nested `if/else` structures obscure the normal path. Guard clauses make it clear: "These are the edge cases. Now here's the main logic." The main path runs at the lowest indentation level.
      
      ### Mechanics
      
      1. Identify each edge case or special condition
      2. Move it to the top of the method as an `if (condition) return earlyValue;`
      3. Remove the corresponding `else` and reduce indentation
      4. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      function payAmount(employee) {
        let result;
        if (employee.isSeparated) {
          result = { amount: 0, reasonCode: 'SEP' };
        } else {
          if (employee.isRetired) {
            result = { amount: 0, reasonCode: 'RET' };
          } else {
            // main calculation
            result = {
              amount: employee.salary * employee.rate,
              reasonCode: 'REG'
            };
          }
        }
        return result;
      }
      ```
      
      **After:**
      ```javascript
      function payAmount(employee) {
        if (employee.isSeparated) return { amount: 0, reasonCode: 'SEP' };
        if (employee.isRetired) return { amount: 0, reasonCode: 'RET' };
      
        return {
          amount: employee.salary * employee.rate,
          reasonCode: 'REG'
        };
      }
      ```
      
      ### Guard Clause Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Null check | `if (input == null) return defaultValue;` |
      | Empty check | `if (items.length === 0) return [];` |
      | Permission check | `if (!user.canEdit) throw new ForbiddenError();` |
      | Boundary check | `if (index < 0 \|\| index >= size) throw new RangeError();` |
      | Status check | `if (order.isCancelled) return zeroPay();` |
      
      ### "One return" vs. Guard Clauses
      
      Some coding standards mandate a single return statement per method. This leads to deeply nested conditionals and temporary result variables. Guard clauses with early returns produce clearer, flatter code. Fowler explicitly recommends guard clauses over single-return for methods with special cases.
      
      ---
      
      ## Replace Conditional with Polymorphism
      
      Replace a conditional that checks a type, status, or category and branches to different behavior with polymorphic classes where each type provides its own implementation.
      
      ### Motivation
      
      This is the gold standard for eliminating type-based conditionals. Instead of one function that knows about every type, each type knows about itself. Adding a new type means adding a new class -- not editing existing conditionals in multiple places (Open/Closed Principle).
      
      ### Mechanics
      
      1. If the conditional is based on a type code, apply Replace Type Code with Subclasses first
      2. Create a base method (possibly abstract) in the superclass
      3. Copy each branch of the conditional into the corresponding subclass as an override
      4. Remove the conditional from the superclass (or make it the default case)
      5. Run tests
      
      ### Example
      
      **Before:**
      ```python
      class Bird:
          def __init__(self, bird_type, voltage=0, coconut_count=0):
              self.type = bird_type
              self.voltage = voltage
              self.coconut_count = coconut_count
      
          def speed(self):
              if self.type == 'european':
                  return 35 - (self.voltage / 10)
              elif self.type == 'african':
                  return 40 - (2 * self.coconut_count)
              elif self.type == 'norwegian_blue':
                  return 0 if self.voltage > 100 else 10 + (self.voltage / 10)
              else:
                  raise ValueError(f"Unknown bird type: {self.type}")
      ```
      
      **After:**
      ```python
      class Bird:
          def speed(self):
              raise NotImplementedError
      
      class EuropeanSwallow(Bird):
          def speed(self):
              return 35 - (self.voltage / 10)
      
      class AfricanSwallow(Bird):
          def speed(self):
              return 40 - (2 * self.coconut_count)
      
      class NorwegianBlueParrot(Bird):
          def speed(self):
              return 0 if self.voltage > 100 else 10 + (self.voltage / 10)
      ```
      
      ### When to Use Polymorphism vs. Keep the Conditional
      
      | Situation | Recommendation |
      |-----------|---------------|
      | Conditional appears in multiple methods | Polymorphism -- types know their own behavior |
      | Only one method has the conditional | May be overkill -- Decompose Conditional may suffice |
      | New types are added frequently | Polymorphism -- Open/Closed Principle |
      | The set of types is fixed and small (e.g., 2-3) | Conditional may be simpler |
      | Behavior varies by a code that changes at runtime | Use Strategy pattern instead of inheritance |
      
      ---
      
      ## Introduce Special Case (Null Object)
      
      Instead of checking for a special case (usually null) in every caller, create a class that encapsulates the special-case behavior.
      
      ### Motivation
      
      `if (customer == null)` checks scattered through the codebase add noise and are easy to forget. A `NullCustomer` or `UnknownCustomer` object responds to all the same methods with safe default behavior.
      
      ### Mechanics
      
      1. Create a subclass or separate class for the special case
      2. Add a method to the superclass or factory that creates the special case (e.g., `Customer.unknown()`)
      3. Implement each method in the special case with the default behavior that callers currently use after their null checks
      4. Change callers to use the special case object instead of null
      5. Remove the null checks from callers
      6. Run tests
      
      ### Example
      
      **Before:**
      ```javascript
      // Scattered throughout the codebase:
      const customerName = (customer !== null) ? customer.name : 'Occupant';
      const billingPlan = (customer !== null) ? customer.billingPlan : BillingPlan.basic();
      const paymentHistory = (customer !== null) ? customer.paymentHistory : new NullPaymentHistory();
      ```
      
      **After:**
      ```javascript
      class UnknownCustomer {
        get name() { return 'Occupant'; }
        get billingPlan() { return BillingPlan.basic(); }
        get paymentHistory() { return new NullPaymentHistory(); }
        get isUnknown() { return true; }
      }
      
      class Customer {
        get isUnknown() { return false; }
        // ... normal implementation
      }
      
      // Callers (no more null checks):
      const customerName = customer.name;
      const billingPlan = customer.billingPlan;
      ```
      
      ### Common Special Cases
      
      | Domain | Special Case Object | Default Behavior |
      |--------|-------------------|------------------|
      | Customer | `UnknownCustomer` | Returns "Occupant", basic plan |
      | Currency | `NullMoney` | Zero amount, no currency |
      | Logger | `NullLogger` | Silently discards all messages |
      | Permission | `DeniedPermission` | Returns false for all checks |
      | Config | `DefaultConfig` | Returns sensible defaults |
      | User | `AnonymousUser` | Read-only, no privileges |
      
      ---
      
      ## Introduce Assertion
      
      Make an assumption explicit by inserting an assertion that will fail fast if the assumption is violated.
      
      ### Motivation
      
      Assertions document what the code expects to be true. They are executable documentation that catches bugs during development. Unlike comments, assertions are verified by the runtime.
      
      ### Mechanics
      
      1. Identify an assumption in the code (a condition that should always be true)
      2. Insert an assertion at the point where the assumption is made
      3. Ensure the assertion does not have side effects
      4. Run tests (they should still pass -- if an assertion fails, you found a bug)
      
      ### Example
      
      **Before:**
      ```python
      def apply_discount(product, discount_rate):
          # discount should be between 0 and 1
          price = product.base_price * (1 - discount_rate)
          return price
      ```
      
      **After:**
      ```python
      def apply_discount(product, discount_rate):
          assert 0 <= discount_rate <= 1, f"Discount rate must be 0-1, got {discount_rate}"
          price = product.base_price * (1 - discount_rate)
          return price
      ```
      
      ### Assertion Guidelines
      
      | Guideline | Rationale |
      |-----------|-----------|
      | Never use assertions for input validation | Assertions can be disabled in production; use exceptions for untrusted input |
      | Use assertions for programmer errors | Conditions that should never occur if the code is correct |
      | Keep assertion messages descriptive | Include the actual value and the expected constraint |
      | Don't put side effects in assertions | `assert items.remove(x)` breaks when assertions are disabled |
      
      ---
      
      ## Decision Guide: Which Conditional Refactoring to Use
      
      | Situation | Refactoring |
      |-----------|-------------|
      | Long, complex condition expression | Decompose Conditional |
      | Multiple conditions lead to same result | Consolidate Conditional Expression |
      | Nested if/else with special cases | Replace Nested Conditional with Guard Clauses |
      | Switch/if on type code in multiple places | Replace Conditional with Polymorphism |
      | Null checks scattered everywhere | Introduce Special Case (Null Object) |
      | Hidden assumption in code logic | Introduce Assertion |
      | Condition appears once, set of types is small | Keep the conditional, but Decompose it |
      | Condition varies at runtime | Use Strategy pattern |
      
    • smell-catalog.md 13.2 KB
      # Code Smell Catalog
      
      A comprehensive catalog of code smells organized by family. Each smell includes a description, detection heuristics, and the named refactorings that fix it.
      
      
      ## Table of Contents
      1. [What Is a Code Smell?](#what-is-a-code-smell)
      2. [Family 1: Bloaters](#family-1-bloaters)
      3. [Family 2: Object-Orientation Abusers](#family-2-object-orientation-abusers)
      4. [Family 3: Change Preventers](#family-3-change-preventers)
      5. [Family 4: Dispensables](#family-4-dispensables)
      6. [Family 5: Couplers](#family-5-couplers)
      7. [Smell-to-Refactoring Quick Reference](#smell-to-refactoring-quick-reference)
      
      ---
      
      ## What Is a Code Smell?
      
      A code smell is a surface indication that usually corresponds to a deeper structural problem. Smells are not bugs -- the code works correctly -- but they make the code harder to understand, extend, and maintain. The term was coined by Kent Beck and popularized by Martin Fowler.
      
      **Key principles:**
      - Smells are heuristics, not rules -- use judgment
      - A smell that causes no real problem in context can be left alone
      - Smells cluster: fixing one often reveals others nearby
      - The "smell → refactoring" mapping is many-to-many; one smell may require several refactorings
      
      ---
      
      ## Family 1: Bloaters
      
      Smells where code grows too large to work with effectively.
      
      ### Long Method
      
      **Description:** A method that tries to do too much. The longer a method is, the harder it is to understand, test, and reuse.
      
      **Detection heuristics:**
      - More than 10-15 lines of executable code
      - Multiple levels of indentation
      - Comments separating "sections" within the method
      - Multiple responsibilities visible in one scan
      - Difficulty naming the method because it does several things
      
      **Typical fixes:**
      - Extract Method -- pull each logical section into a named method
      - Replace Temp with Query -- eliminate temporaries that block extraction
      - Replace Method with Method Object -- when extraction is blocked by tangled local variables
      - Decompose Conditional -- when the length comes from complex branching
      
      **Example smell:**
      ```
      function processOrder(order) {
        // validate order
        if (!order.items || order.items.length === 0) { ... }
        if (!order.customer) { ... }
        // calculate totals
        let subtotal = 0;
        for (const item of order.items) { subtotal += item.price * item.qty; }
        let tax = subtotal * 0.08;
        let shipping = subtotal > 100 ? 0 : 9.99;
        // apply discounts
        if (order.customer.isPremium) { ... }
        // save to database
        db.save({ ...order, subtotal, tax, shipping });
        // send confirmation
        emailService.send(order.customer.email, ...);
      }
      ```
      
      Each comment block should be its own method: `validateOrder()`, `calculateTotals()`, `applyDiscounts()`, `saveOrder()`, `sendConfirmation()`.
      
      ### Large Class
      
      **Description:** A class that has too many fields, too many methods, or too many responsibilities. Often called a "God Class" or "Blob."
      
      **Detection heuristics:**
      - More than 200-300 lines
      - More than 10-15 fields
      - Fields that cluster into subgroups (e.g., address fields, billing fields)
      - Methods that only use a subset of the fields
      - The class name is vague (`Manager`, `Handler`, `Processor`, `Utils`)
      
      **Typical fixes:**
      - Extract Class -- split along the axis of change
      - Extract Subclass -- when behavior varies by type
      - Replace Data Value with Object -- when field clusters represent a concept
      
      ### Long Parameter List
      
      **Description:** A method that takes more than three or four parameters, making calls confusing and error-prone.
      
      **Detection heuristics:**
      - More than 3-4 parameters
      - Boolean parameters that switch behavior
      - Parameters that always travel together
      - Callers passing `null` for unused parameters
      
      **Typical fixes:**
      - Introduce Parameter Object -- group related params into `DateRange`, `Address`, `Options`
      - Preserve Whole Object -- pass the object instead of extracting its fields
      - Replace Parameter with Method -- have the method fetch data it needs
      
      ### Data Clumps
      
      **Description:** Groups of variables that appear together in multiple places -- method parameters, field declarations, or local variables.
      
      **Detection heuristics:**
      - Same three or more fields appear together in multiple classes
      - Same group of parameters appears in multiple method signatures
      - Deleting one member of the group would make no sense without the others
      
      **Typical fixes:**
      - Extract Class -- create a new class for the clump (`Address`, `DateRange`, `Coordinates`)
      - Introduce Parameter Object -- replace parameter groups with the new class
      - Preserve Whole Object -- pass the object instead of its decomposed fields
      
      ### Primitive Obsession
      
      **Description:** Using primitive types (strings, ints, arrays) to represent domain concepts instead of small objects.
      
      **Detection heuristics:**
      - Constants or magic numbers used to represent types (`int ADMIN = 1`)
      - Strings used for structured data (phone numbers, zip codes, currencies)
      - Arrays or tuples used instead of named structures
      - Validation logic for the same type scattered across multiple locations
      
      **Typical fixes:**
      - Replace Data Value with Object -- `String email` becomes `EmailAddress`
      - Replace Type Code with Subclasses -- when the type code drives behavior
      - Replace Type Code with Strategy -- when subclassing is impractical
      - Replace Magic Number with Symbolic Constant -- names instead of numbers
      
      ---
      
      ## Family 2: Object-Orientation Abusers
      
      Smells where object-oriented features are used incorrectly or not at all.
      
      ### Switch Statements
      
      **Description:** The same switch/case or if/else chain on a type code appears in multiple places. When a new type is added, every switch must be updated.
      
      **Detection heuristics:**
      - `switch` on a type or status field that appears in more than one place
      - `if/else if` chain checking `instanceof` or type strings
      - Adding a new type requires editing multiple files
      
      **Typical fixes:**
      - Replace Conditional with Polymorphism -- each type implements its own behavior
      - Replace Type Code with Subclasses + Replace Conditional with Polymorphism
      - Replace Type Code with Strategy when the type can change at runtime
      
      **Example:**
      ```
      // SMELL: same switch in calculatePay(), generateReport(), getPermissions()
      switch (employee.type) {
        case 'engineer': return basePay;
        case 'manager': return basePay + bonus;
        case 'salesperson': return basePay + commission;
      }
      
      // FIX: polymorphism
      class Engineer extends Employee {
        calculatePay() { return this.basePay; }
      }
      class Manager extends Employee {
        calculatePay() { return this.basePay + this.bonus; }
      }
      ```
      
      ### Refused Bequest
      
      **Description:** A subclass inherits methods or data it does not want. It overrides parent methods to do nothing or throws "not supported" exceptions.
      
      **Detection heuristics:**
      - Subclass overrides a method to do nothing or throw
      - Subclass uses only a small fraction of inherited methods
      - The "is-a" relationship feels forced
      
      **Typical fixes:**
      - Push Down Method / Push Down Field -- move unwanted members to the sibling that actually uses them
      - Replace Inheritance with Delegation -- the child holds a reference to the parent instead of extending it
      
      ### Alternative Classes with Different Interfaces
      
      **Description:** Two classes do essentially the same job but have different method names and signatures, preventing interchangeability.
      
      **Detection heuristics:**
      - Two classes with similar purpose but different method names
      - Callers choose between them but can't treat them polymorphically
      - Duplication of logic because no shared interface exists
      
      **Typical fixes:**
      - Rename Method -- align names across both classes
      - Extract Superclass or Extract Interface -- define a shared contract
      - Move Method -- equalize what each class offers
      
      ---
      
      ## Family 3: Change Preventers
      
      Smells that make changes expensive by scattering related logic.
      
      ### Divergent Change
      
      **Description:** One class changes for multiple unrelated reasons. It is the opposite of the Single Responsibility Principle.
      
      **Detection heuristics:**
      - You edit the same class for different kinds of changes (new database, new report format, new business rule)
      - The class has methods that cluster into groups with no interaction between them
      - Different team members edit the same file for different features
      
      **Typical fixes:**
      - Extract Class -- split the class along its axes of change
      - Each resulting class should change for exactly one reason
      
      ### Shotgun Surgery
      
      **Description:** A single logical change requires edits in many different classes. It is the opposite of Divergent Change.
      
      **Detection heuristics:**
      - A small functional change touches 5+ files
      - A new field must be added to multiple classes
      - A format change requires edits in scattered locations
      
      **Typical fixes:**
      - Move Method / Move Field -- consolidate related logic into one class
      - Inline Class -- if scattered pieces are too small, merge them into the class that should own the responsibility
      
      ---
      
      ## Family 4: Dispensables
      
      Smells where something exists but shouldn't.
      
      ### Lazy Class
      
      **Description:** A class that does too little to justify its existence. Each class costs complexity; if it doesn't carry its weight, merge it.
      
      **Typical fixes:** Inline Class, Collapse Hierarchy
      
      ### Dead Code
      
      **Description:** Code that is never executed -- unreachable branches, unused variables, unneeded parameters, methods no one calls.
      
      **Typical fixes:** Delete it. Version control remembers.
      
      ### Speculative Generality
      
      **Description:** Abstractions, parameters, hooks, or classes created "in case we need them someday." YAGNI -- You Aren't Gonna Need It.
      
      **Detection heuristics:**
      - Abstract classes with only one subclass
      - Parameters that are always passed the same value
      - Methods that are only called by tests
      - Framework infrastructure with no current use
      
      **Typical fixes:**
      - Collapse Hierarchy -- remove unneeded abstract class
      - Remove Parameter -- delete unused params
      - Inline Class / Inline Method -- collapse unneeded indirection
      
      ### Duplicate Code
      
      **Description:** The same or nearly identical code structure appears in more than one place. The most common and most expensive smell.
      
      **Detection heuristics:**
      - Copy-pasted blocks with minor variations
      - Methods in different classes that do the same thing
      - Conditional branches with identical bodies
      
      **Typical fixes:**
      - Extract Method -- share the common code
      - Pull Up Method -- move shared method to a common base class
      - Extract Superclass / Extract Class -- when duplication spans classes
      - Form Template Method -- when method structure is identical but details differ
      
      ---
      
      ## Family 5: Couplers
      
      Smells where classes are too tightly bound to each other.
      
      ### Feature Envy
      
      **Description:** A method that uses more features (fields and methods) of another class than its own. It "envies" the other class's data.
      
      **Detection heuristics:**
      - A method that calls 3+ getters on one foreign object
      - A method that could be moved to the other class and would need fewer parameters
      
      **Typical fixes:**
      - Move Method -- relocate the method to the class it envies
      - Extract Method + Move Method -- extract the envious part, then move it
      
      ### Inappropriate Intimacy
      
      **Description:** Two classes are overly entangled -- accessing each other's private details, forming a bidirectional dependency.
      
      **Typical fixes:**
      - Move Method / Move Field to reduce the cross-boundary traffic
      - Extract Class to put the shared concern in a neutral place
      - Replace Inheritance with Delegation when subclass accesses too many parent internals
      
      ### Message Chains
      
      **Description:** A client asks object A for B, then asks B for C, then asks C for D: `a.getB().getC().getD()`. The client is coupled to the entire navigation structure.
      
      **Typical fixes:**
      - Hide Delegate -- have A provide the answer directly
      - Extract Method + Move Method -- push the chain into the object that should know the answer
      
      ### Middle Man
      
      **Description:** A class whose methods do nothing but delegate to another class. It adds indirection without value.
      
      **Detection heuristics:**
      - More than half of a class's methods are one-line delegations
      - The class has no logic of its own
      
      **Typical fixes:**
      - Remove Middle Man -- let the client call the delegate directly
      - Inline Method -- merge the trivial forwarding methods into the caller
      
      ---
      
      ## Smell-to-Refactoring Quick Reference
      
      | Smell | Primary Refactoring | Secondary Refactoring |
      |-------|--------------------|-----------------------|
      | Long Method | Extract Method | Replace Temp with Query |
      | Large Class | Extract Class | Extract Subclass |
      | Long Parameter List | Introduce Parameter Object | Preserve Whole Object |
      | Data Clumps | Extract Class | Introduce Parameter Object |
      | Primitive Obsession | Replace Data Value with Object | Replace Type Code with Subclasses |
      | Switch Statements | Replace Conditional with Polymorphism | Replace Type Code with Strategy |
      | Refused Bequest | Replace Inheritance with Delegation | Push Down Method |
      | Divergent Change | Extract Class | -- |
      | Shotgun Surgery | Move Method / Move Field | Inline Class |
      | Lazy Class | Inline Class | Collapse Hierarchy |
      | Dead Code | Delete it | -- |
      | Speculative Generality | Collapse Hierarchy | Inline Class |
      | Duplicate Code | Extract Method | Pull Up Method |
      | Feature Envy | Move Method | Extract Method + Move Method |
      | Inappropriate Intimacy | Move Method / Move Field | Extract Class |
      | Message Chains | Hide Delegate | Extract Method |
      | Middle Man | Remove Middle Man | Inline Method |
      
  • SKILL.md 15.6 KB
    ---
    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.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related