askill
refactoring-patterns

refactoring-patternsSafety 90Repository

Safe refactoring techniques — extract method, rename, move, inline, and structural patterns. Includes code smell identification and transformation recipes. Use when refactoring code or improving structure.

3 stars
1.2k downloads
Updated 1/30/2026

Package Files

Loading files...
SKILL.md

Refactoring Patterns

Safety Rules

Before any refactoring, verify all of the following:

  • Tests exist covering the code you will change
  • All tests pass before you start
  • Perform one transformation at a time
  • Run tests after every single transformation
  • Commit after each successful step
  • Name every transformation in your commit message (e.g., "refactor: Extract Method -- validateEmail")
  • Never change behavior and structure in the same commit
  • If a step breaks tests, revert immediately and try a smaller step

Code Smell Catalog

SmellSymptomsTypical Refactoring
Long MethodMethod over 20 lines; multiple levels of abstractionExtract Method
Large ClassClass over 300 lines; too many responsibilitiesExtract Class
Feature EnvyMethod uses more data from another class than its ownMove Method
Data ClumpSame group of fields/params appear together repeatedlyIntroduce Parameter Object
Primitive ObsessionUsing primitives instead of small domain objectsReplace Primitive with Value Object
Shotgun SurgeryOne change requires edits across many filesMove Method, Inline Class
Divergent ChangeOne class changed for many different reasonsExtract Class
Dead CodeUnreachable code, unused variables, commented-out blocksRemove Dead Code
Long Parameter ListMethod takes more than 3 parametersIntroduce Parameter Object
Switch StatementsRepeated switch/if-else on the same type fieldReplace Conditional with Polymorphism
Duplicate CodeSame logic in multiple placesExtract Method, Extract Superclass
Magic NumbersUnexplained literal values in logicReplace Magic Number with Named Constant

Smell Examples

Long Method:

function processOrder(order: Order) {
  // validate (10 lines) + calculate totals (15 lines)
  // apply discounts (12 lines) + check inventory (8 lines)
  // create invoice (10 lines) + send notification (6 lines)
}

Feature Envy:

class Order {
  calculateShipping() {
    if (this.customer.address.country === "US") {
      if (this.customer.tier === "premium") return 0;
      return this.customer.address.isRemote ? 15 : 5;
    }
    return 25;
  }
}

Data Clump:

function createUser(name: string, street: string, city: string, state: string, zip: string) {}
function updateAddress(street: string, city: string, state: string, zip: string) {}

Named Refactorings

RefactoringInputOutput
Extract MethodCode block inside a methodNew method + call site
Extract ClassFields and methods from a classNew class with focused responsibility
Inline MethodTrivial methodBody placed at all call sites
RenameUnclear nameIntention-revealing name
Move MethodMethod in wrong classMethod in the class that owns the data
Introduce Parameter ObjectMultiple related parametersSingle object parameter
Replace Conditional with PolymorphismType-based switch/if-elseSubclasses with overridden method
Replace Magic Number with Named ConstantLiteral number in codeNamed constant
Decompose ConditionalComplex boolean expressionNamed methods for conditions
Remove Dead CodeUnused codeDeletion

Extract Method

Recipe: (1) Identify code to extract. (2) Create method with intention-revealing name. (3) Copy code in. (4) Local variables become locals; outer scope reads become parameters; modified-and-used-after become return values. (5) Replace original with call. (6) Run tests.

// Before
function printInvoice(invoice: Invoice) {
  console.log("=== Invoice ===");
  let total = 0;
  for (const item of invoice.items) { total += item.price * item.quantity; }
  const tax = total * 0.08;
  console.log(`Total: ${total + tax}`);
}

// After
function printInvoice(invoice: Invoice) {
  console.log("=== Invoice ===");
  console.log(`Total: ${calculateGrandTotal(invoice.items)}`);
}

function calculateGrandTotal(items: InvoiceItem[]): number {
  const subtotal = items.reduce((sum, i) => sum + i.price * i.quantity, 0);
  return subtotal + subtotal * 0.08;
}

Extract Class

Recipe: (1) Identify cohesive fields and methods. (2) Create new class. (3) Move fields. (4) Move methods. (5) Delegate from original. (6) Run tests after each move.

// Before
class User {
  name: string; email: string;
  street: string; city: string; state: string; zip: string;
  fullAddress(): string { return `${this.street}, ${this.city}, ${this.state} ${this.zip}`; }
}

// After
class Address {
  constructor(public street: string, public city: string, public state: string, public zip: string) {}
  full(): string { return `${this.street}, ${this.city}, ${this.state} ${this.zip}`; }
}

class User {
  name: string; email: string; address: Address;
}

Introduce Parameter Object

Recipe: (1) Create type for the parameter group. (2) Add object parameter. (3) Update callers. (4) Remove old params. (5) Run tests.

// Before
function searchProducts(minPrice: number, maxPrice: number, category: string, inStock: boolean) {}

// After
interface ProductFilter { minPrice: number; maxPrice: number; category: string; inStock: boolean; }
function searchProducts(filter: ProductFilter) {}

Replace Conditional with Polymorphism

Recipe: (1) Create base interface with the varying method. (2) Create subclass per case. (3) Move case logic into subclass. (4) Replace conditional with method call. (5) Run tests.

// Before
function calculateArea(shape: Shape): number {
  switch (shape.type) {
    case "circle":    return Math.PI * shape.radius ** 2;
    case "rectangle": return shape.width * shape.height;
    case "triangle":  return (shape.base * shape.height) / 2;
  }
}

// After
interface Shape { area(): number; }
class Circle implements Shape {
  constructor(private radius: number) {}
  area() { return Math.PI * this.radius ** 2; }
}
class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}
  area() { return this.width * this.height; }
}
class Triangle implements Shape {
  constructor(private base: number, private height: number) {}
  area() { return (this.base * this.height) / 2; }
}

Decompose Conditional

Recipe: (1) Extract condition into a named method. (2) Extract then-branch if non-trivial. (3) Extract else-branch if non-trivial. (4) Run tests.

// Before
if (date.getMonth() >= 5 && date.getMonth() <= 8 && !isHoliday(date)) {
  rate = quantity * summerRate;
} else {
  rate = quantity * regularRate + serviceFee;
}

// After
if (isSummerBusinessDay(date)) { rate = summerCharge(quantity); }
else { rate = regularCharge(quantity); }

Remove Dead Code

Recipe: (1) Verify truly unused (search references, check exports). (2) Delete. (3) Run tests. (4) Commit as refactor: Remove Dead Code -- <what>.

Dead code includes: unreachable branches, unused functions, commented-out blocks, unused variables and imports, removed feature flag code. Never comment out code "for later" -- version control preserves history.

Strangler Fig Pattern

For large-scale refactors that cannot be completed in a single step:

  1. Identify the boundary of the legacy code to replace
  2. Build the new implementation alongside the old one
  3. Route traffic incrementally using a facade or feature flag
  4. Verify each increment in production
  5. Remove the old implementation once all traffic uses the new code
// Step 1: Facade delegates to old code
class PaymentProcessor {
  process(payment: Payment): Result { return this.legacyProcessor.process(payment); }
}

// Step 2-3: Route with feature flag
class PaymentProcessor {
  process(payment: Payment): Result {
    if (featureFlag.isEnabled("new-payment-processor"))
      return this.newProcessor.process(payment);
    return this.legacyProcessor.process(payment);
  }
}

// Step 4-5: After verification, remove old code
class PaymentProcessor {
  process(payment: Payment): Result { return this.newProcessor.process(payment); }
}

Strangler Fig Checklist

  • Both old and new paths are tested
  • Rollback is possible at every stage by toggling the flag
  • Monitoring and alerts cover both paths
  • Old code is deleted only after the new path is proven in production

Refactoring Workflow Summary

1. Identify the smell
2. Choose the named refactoring
3. Verify tests pass (write them if missing)
4. Apply one transformation
5. Run tests
6. Commit: "refactor: <Refactoring Name> -- <target>"
7. Repeat from step 4 until the smell is resolved

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

87/100Analyzed 2/23/2026

Comprehensive refactoring skill with clear safety rules, code smell catalog, and detailed step-by-step recipes with TypeScript examples. Well-structured with tables and consistent formatting. Slight mismatch between tags (ci-cd, github-actions, observability) and refactoring content, but content quality is high and highly actionable.

90
90
88
85
92

Metadata

Licenseunknown
Version-
Updated1/30/2026
PublisherClaude-Code-Community-Ireland

Tags

ci-cdgithub-actionsobservability