# Refactoring Assistant

> Helps improve code quality through systematic refactoring, applying SOLID principles, DRY patterns, and design patterns. Use when cleaning up legacy code, reducing complexity, eliminating code smells, improving maintainability, or restructuring codebases for better architecture.

- Skill: `mehdiozdemir/refactoring-assistant` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mehdiozdemir/refactoring-assistant`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mehdiozdemir/refactoring-assistant/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: mehdiozdemir (https://skillmd.com/u/mehdiozdemir)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/mehdiozdemir/refactoring-assistant

---


# Refactoring Assistant Skill

This skill guides systematic code improvement through proven refactoring techniques. Use this whenever you need to clean up code, reduce technical debt, improve structure, or apply better design patterns.

## Core Refactoring Principles

### 1. **SOLID Principles**

| Principle | Description | Violation Signs |
|-----------|-------------|-----------------|
| **S**ingle Responsibility | One class = one reason to change | Large classes, mixed concerns |
| **O**pen/Closed | Open for extension, closed for modification | Frequent edits to existing code |
| **L**iskov Substitution | Subtypes must be substitutable | Broken inheritance, type checks |
| **I**nterface Segregation | Many specific interfaces > one general | Fat interfaces, unused methods |
| **D**ependency Inversion | Depend on abstractions, not concretions | Hard-coded dependencies |

### 2. **DRY (Don't Repeat Yourself)**
- Eliminate duplicate code
- Extract common logic into reusable components
- Use configuration over hardcoding
- Single source of truth for data

### 3. **KISS (Keep It Simple, Stupid)**
- Prefer simple solutions over clever ones
- Avoid premature optimization
- Reduce unnecessary complexity
- Write readable, self-documenting code

### 4. **YAGNI (You Ain't Gonna Need It)**
- Don't add functionality until needed
- Remove unused code
- Avoid speculative generality
- Build for today's requirements

## Code Smells Catalog

### Bloaters
Code that has grown excessively large:

| Smell | Description | Refactoring |
|-------|-------------|-------------|
| **Long Method** | Method > 20-30 lines | Extract Method |
| **Large Class** | Class doing too much | Extract Class |
| **Primitive Obsession** | Overuse of primitives | Replace with Value Object |
| **Long Parameter List** | > 3-4 parameters | Parameter Object, Builder |
| **Data Clumps** | Groups of data appearing together | Extract Class |

### Object-Orientation Abusers
Misuse of OOP principles:

| Smell | Description | Refactoring |
|-------|-------------|-------------|
| **Switch Statements** | Complex conditionals | Replace with Polymorphism |
| **Temporary Field** | Fields only sometimes used | Extract Class |
| **Refused Bequest** | Subclass not using parent methods | Replace Inheritance with Delegation |
| **Alternative Classes** | Similar classes, different interfaces | Rename, Merge Classes |

### Change Preventers
Code that makes changes difficult:

| Smell | Description | Refactoring |
|-------|-------------|-------------|
| **Divergent Change** | One class changed for many reasons | Extract Class |
| **Shotgun Surgery** | One change affects many classes | Move Method, Inline Class |
| **Parallel Inheritance** | Subclass requires parallel subclass | Move Method, Move Field |

### Dispensables
Code that could be removed:

| Smell | Description | Refactoring |
|-------|-------------|-------------|
| **Comments** | Excessive comments hiding bad code | Extract Method, Rename |
| **Duplicate Code** | Same code in multiple places | Extract Method/Class |
| **Dead Code** | Unreachable/unused code | Delete it |
| **Speculative Generality** | Unused abstractions | Collapse Hierarchy |
| **Lazy Class** | Class doing too little | Inline Class |

### Couplers
Excessive coupling between classes:

| Smell | Description | Refactoring |
|-------|-------------|-------------|
| **Feature Envy** | Method uses another class more | Move Method |
| **Inappropriate Intimacy** | Classes too dependent | Move Method, Extract Class |
| **Message Chains** | a.b().c().d() | Hide Delegate |
| **Middle Man** | Class only delegates | Remove Middle Man |

## Refactoring Patterns

### Extract Method
Transform long methods into smaller, focused ones.

**Before:**
```python
def process_order(order):
    # Validate order
    if not order.items:
        raise ValueError("Order must have items")
    if order.total < 0:
        raise ValueError("Invalid total")
    
    # Calculate discounts
    discount = 0
    if order.customer.is_premium:
        discount = order.total * 0.1
    if order.total > 100:
        discount += order.total * 0.05
    
    # Apply tax
    subtotal = order.total - discount
    tax = subtotal * 0.08
    final_total = subtotal + tax
    
    # Send confirmation
    email = f"Order confirmed. Total: ${final_total}"
    send_email(order.customer.email, "Order Confirmation", email)
    
    return final_total
```

**After:**
```python
def process_order(order):
    validate_order(order)
    discount = calculate_discount(order)
    final_total = apply_tax(order.total - discount)
    send_order_confirmation(order.customer, final_total)
    return final_total

def validate_order(order):
    if not order.items:
        raise ValueError("Order must have items")
    if order.total < 0:
        raise ValueError("Invalid total")

def calculate_discount(order):
    discount = 0
    if order.customer.is_premium:
        discount = order.total * 0.1
    if order.total > 100:
        discount += order.total * 0.05
    return discount

def apply_tax(subtotal, tax_rate=0.08):
    return subtotal * (1 + tax_rate)

def send_order_confirmation(customer, total):
    email = f"Order confirmed. Total: ${total}"
    send_email(customer.email, "Order Confirmation", email)
```

### Extract Class
Split a class with multiple responsibilities.

**Before:**
```python
class User:
    def __init__(self, name, email, street, city, zip_code, country):
        self.name = name
        self.email = email
        self.street = street
        self.city = city
        self.zip_code = zip_code
        self.country = country
    
    def get_full_address(self):
        return f"{self.street}, {self.city}, {self.zip_code}, {self.country}"
    
    def validate_address(self):
        return bool(self.street and self.city and self.zip_code)
    
    def format_mailing_label(self):
        return f"{self.name}\n{self.get_full_address()}"
```

**After:**
```python
class Address:
    def __init__(self, street, city, zip_code, country):
        self.street = street
        self.city = city
        self.zip_code = zip_code
        self.country = country
    
    def get_full_address(self):
        return f"{self.street}, {self.city}, {self.zip_code}, {self.country}"
    
    def is_valid(self):
        return bool(self.street and self.city and self.zip_code)

class User:
    def __init__(self, name, email, address: Address):
        self.name = name
        self.email = email
        self.address = address
    
    def format_mailing_label(self):
        return f"{self.name}\n{self.address.get_full_address()}"
```

### Replace Conditional with Polymorphism
Replace complex conditionals with inheritance.

**Before:**
```python
class PaymentProcessor:
    def process_payment(self, payment_type, amount, details):
        if payment_type == "credit_card":
            # Validate card
            if not self._validate_card(details["card_number"]):
                raise ValueError("Invalid card")
            # Charge card
            response = self._charge_card(details["card_number"], amount)
            return response
        
        elif payment_type == "paypal":
            # Authenticate with PayPal
            token = self._get_paypal_token(details["email"])
            # Process PayPal payment
            response = self._paypal_charge(token, amount)
            return response
        
        elif payment_type == "bank_transfer":
            # Validate bank details
            if not self._validate_iban(details["iban"]):
                raise ValueError("Invalid IBAN")
            # Initiate transfer
            response = self._initiate_transfer(details["iban"], amount)
            return response
        
        else:
            raise ValueError(f"Unknown payment type: {payment_type}")
```

**After:**
```python
from abc import ABC, abstractmethod

class PaymentMethod(ABC):
    @abstractmethod
    def validate(self, details: dict) -> bool:
        pass
    
    @abstractmethod
    def process(self, amount: float, details: dict) -> dict:
        pass

class CreditCardPayment(PaymentMethod):
    def validate(self, details):
        return self._validate_card(details["card_number"])
    
    def process(self, amount, details):
        if not self.validate(details):
            raise ValueError("Invalid card")
        return self._charge_card(details["card_number"], amount)

class PayPalPayment(PaymentMethod):
    def validate(self, details):
        return bool(details.get("email"))
    
    def process(self, amount, details):
        token = self._get_paypal_token(details["email"])
        return self._paypal_charge(token, amount)

class BankTransferPayment(PaymentMethod):
    def validate(self, details):
        return self._validate_iban(details["iban"])
    
    def process(self, amount, details):
        if not self.validate(details):
            raise ValueError("Invalid IBAN")
        return self._initiate_transfer(details["iban"], amount)

class PaymentProcessor:
    def __init__(self):
        self._methods = {
            "credit_card": CreditCardPayment(),
            "paypal": PayPalPayment(),
            "bank_transfer": BankTransferPayment(),
        }
    
    def process_payment(self, payment_type, amount, details):
        method = self._methods.get(payment_type)
        if not method:
            raise ValueError(f"Unknown payment type: {payment_type}")
        return method.process(amount, details)
```

### Introduce Parameter Object
Replace long parameter lists with an object.

**Before:**
```python
def create_report(
    title,
    author,
    start_date,
    end_date,
    include_charts,
    include_summary,
    page_size,
    orientation,
    department,
    category
):
    # Create report with all these parameters...
    pass

def email_report(
    title,
    author,
    start_date,
    end_date,
    include_charts,
    include_summary,
    recipients
):
    # Email report with overlapping parameters...
    pass
```

**After:**
```python
from dataclasses import dataclass
from datetime import date
from typing import Optional

@dataclass
class ReportConfig:
    title: str
    author: str
    start_date: date
    end_date: date
    include_charts: bool = True
    include_summary: bool = True

@dataclass
class PrintConfig:
    page_size: str = "A4"
    orientation: str = "portrait"

@dataclass
class ReportFilter:
    department: Optional[str] = None
    category: Optional[str] = None

def create_report(
    config: ReportConfig,
    print_config: PrintConfig,
    filter: ReportFilter
):
    # Much cleaner!
    pass

def email_report(
    config: ReportConfig,
    recipients: list[str]
):
    # Reuses ReportConfig
    pass
```

### Replace Magic Numbers/Strings with Constants

**Before:**
```python
def calculate_shipping(weight, distance):
    if weight < 1:
        base_cost = 5.99
    elif weight < 5:
        base_cost = 9.99
    else:
        base_cost = 14.99
    
    if distance > 500:
        base_cost *= 1.5
    
    if distance > 1000:
        base_cost *= 2.0
    
    return base_cost

def get_status_message(status):
    if status == 1:
        return "Pending"
    elif status == 2:
        return "Processing"
    elif status == 3:
        return "Shipped"
    elif status == 4:
        return "Delivered"
```

**After:**
```python
from enum import Enum, auto
from dataclasses import dataclass

# Weight-based pricing
class ShippingTier:
    LIGHT_WEIGHT_LIMIT = 1  # kg
    MEDIUM_WEIGHT_LIMIT = 5  # kg
    
    LIGHT_COST = 5.99
    MEDIUM_COST = 9.99
    HEAVY_COST = 14.99

# Distance multipliers
class DistanceMultiplier:
    LONG_DISTANCE_THRESHOLD = 500  # km
    VERY_LONG_DISTANCE_THRESHOLD = 1000  # km
    
    LONG_DISTANCE_MULTIPLIER = 1.5
    VERY_LONG_DISTANCE_MULTIPLIER = 2.0

class OrderStatus(Enum):
    PENDING = auto()
    PROCESSING = auto()
    SHIPPED = auto()
    DELIVERED = auto()
    
    @property
    def display_name(self):
        return self.name.capitalize()

def calculate_shipping(weight: float, distance: float) -> float:
    if weight < ShippingTier.LIGHT_WEIGHT_LIMIT:
        base_cost = ShippingTier.LIGHT_COST
    elif weight < ShippingTier.MEDIUM_WEIGHT_LIMIT:
        base_cost = ShippingTier.MEDIUM_COST
    else:
        base_cost = ShippingTier.HEAVY_COST
    
    if distance > DistanceMultiplier.VERY_LONG_DISTANCE_THRESHOLD:
        base_cost *= DistanceMultiplier.VERY_LONG_DISTANCE_MULTIPLIER
    elif distance > DistanceMultiplier.LONG_DISTANCE_THRESHOLD:
        base_cost *= DistanceMultiplier.LONG_DISTANCE_MULTIPLIER
    
    return base_cost

def get_status_message(status: OrderStatus) -> str:
    return status.display_name
```

### Dependency Injection
Replace hard-coded dependencies with injected ones.

**Before:**
```python
class UserService:
    def __init__(self):
        # Hard-coded dependencies - difficult to test
        self.db = PostgresDatabase("localhost", "users_db")
        self.cache = RedisCache("localhost:6379")
        self.mailer = SmtpMailer("smtp.gmail.com")
    
    def create_user(self, data):
        user = self.db.insert("users", data)
        self.cache.set(f"user:{user.id}", user)
        self.mailer.send(user.email, "Welcome!")
        return user
```

**After:**
```python
from abc import ABC, abstractmethod

# Define interfaces (abstractions)
class Database(ABC):
    @abstractmethod
    def insert(self, table: str, data: dict) -> dict:
        pass

class Cache(ABC):
    @abstractmethod
    def set(self, key: str, value: any) -> None:
        pass

class Mailer(ABC):
    @abstractmethod
    def send(self, to: str, message: str) -> None:
        pass

# Concrete implementations
class PostgresDatabase(Database):
    def __init__(self, host: str, db_name: str):
        self.host = host
        self.db_name = db_name
    
    def insert(self, table, data):
        # PostgreSQL-specific implementation
        pass

class RedisCache(Cache):
    def __init__(self, url: str):
        self.url = url
    
    def set(self, key, value):
        # Redis-specific implementation
        pass

# Service with injected dependencies
class UserService:
    def __init__(self, db: Database, cache: Cache, mailer: Mailer):
        self.db = db
        self.cache = cache
        self.mailer = mailer
    
    def create_user(self, data):
        user = self.db.insert("users", data)
        self.cache.set(f"user:{user.id}", user)
        self.mailer.send(user.email, "Welcome!")
        return user

# Easy to test with mocks!
class MockDatabase(Database):
    def insert(self, table, data):
        return {"id": 1, **data}

# Production usage
service = UserService(
    db=PostgresDatabase("localhost", "users_db"),
    cache=RedisCache("localhost:6379"),
    mailer=SmtpMailer("smtp.gmail.com")
)

# Test usage
test_service = UserService(
    db=MockDatabase(),
    cache=MockCache(),
    mailer=MockMailer()
)
```

### Strategy Pattern
Encapsulate algorithms that can be swapped.

**Before:**
```python
class PriceCalculator:
    def calculate(self, items, customer_type, promo_code):
        total = sum(item.price * item.quantity for item in items)
        
        # Discount logic scattered and hard to maintain
        if customer_type == "premium":
            total *= 0.9  # 10% discount
        elif customer_type == "vip":
            total *= 0.8  # 20% discount
        
        if promo_code == "SUMMER20":
            total *= 0.8
        elif promo_code == "FLASH50":
            total *= 0.5
        elif promo_code == "FREESHIP":
            pass  # Free shipping handled elsewhere
        
        return total
```

**After:**
```python
from abc import ABC, abstractmethod

class DiscountStrategy(ABC):
    @abstractmethod
    def apply(self, total: float) -> float:
        pass

class NoDiscount(DiscountStrategy):
    def apply(self, total):
        return total

class PercentageDiscount(DiscountStrategy):
    def __init__(self, percentage: float):
        self.percentage = percentage
    
    def apply(self, total):
        return total * (1 - self.percentage / 100)

class FixedDiscount(DiscountStrategy):
    def __init__(self, amount: float):
        self.amount = amount
    
    def apply(self, total):
        return max(0, total - self.amount)

class CompositeDiscount(DiscountStrategy):
    def __init__(self, *strategies: DiscountStrategy):
        self.strategies = strategies
    
    def apply(self, total):
        for strategy in self.strategies:
            total = strategy.apply(total)
        return total

# Strategy factory
class DiscountFactory:
    CUSTOMER_DISCOUNTS = {
        "regular": NoDiscount(),
        "premium": PercentageDiscount(10),
        "vip": PercentageDiscount(20),
    }
    
    PROMO_DISCOUNTS = {
        "SUMMER20": PercentageDiscount(20),
        "FLASH50": PercentageDiscount(50),
        "SAVE10": FixedDiscount(10),
    }
    
    @classmethod
    def get_discount(cls, customer_type: str, promo_code: str = None) -> DiscountStrategy:
        strategies = []
        
        customer_discount = cls.CUSTOMER_DISCOUNTS.get(customer_type, NoDiscount())
        strategies.append(customer_discount)
        
        if promo_code:
            promo_discount = cls.PROMO_DISCOUNTS.get(promo_code)
            if promo_discount:
                strategies.append(promo_discount)
        
        if len(strategies) == 1:
            return strategies[0]
        return CompositeDiscount(*strategies)

class PriceCalculator:
    def calculate(self, items, customer_type, promo_code=None):
        total = sum(item.price * item.quantity for item in items)
        discount = DiscountFactory.get_discount(customer_type, promo_code)
        return discount.apply(total)
```

### Null Object Pattern
Replace null checks with a null object.

**Before:**
```python
class UserService:
    def get_user(self, user_id):
        user = self.db.find(user_id)
        return user  # Could be None
    
    def display_welcome(self, user_id):
        user = self.get_user(user_id)
        if user is not None:
            if user.name is not None:
                print(f"Welcome, {user.name}!")
            else:
                print("Welcome!")
            
            if user.preferences is not None:
                if user.preferences.theme is not None:
                    apply_theme(user.preferences.theme)
                else:
                    apply_theme("default")
            else:
                apply_theme("default")
        else:
            print("Welcome, Guest!")
            apply_theme("default")
```

**After:**
```python
from dataclasses import dataclass, field

@dataclass
class Preferences:
    theme: str = "default"
    language: str = "en"
    notifications: bool = True

@dataclass
class User:
    id: str
    name: str
    email: str
    preferences: Preferences = field(default_factory=Preferences)

class NullUser(User):
    """Null Object representing a guest/anonymous user."""
    
    def __init__(self):
        super().__init__(
            id="guest",
            name="Guest",
            email="",
            preferences=Preferences()
        )
    
    def is_authenticated(self):
        return False

class UserService:
    NULL_USER = NullUser()
    
    def get_user(self, user_id) -> User:
        user = self.db.find(user_id)
        return user if user else self.NULL_USER
    
    def display_welcome(self, user_id):
        user = self.get_user(user_id)
        # No null checks needed!
        print(f"Welcome, {user.name}!")
        apply_theme(user.preferences.theme)
```

## Refactoring Process

### Step 1: Identify the Smell
- Review code for patterns listed above
- Check complexity metrics (cyclomatic complexity, LOC)
- Look for repeated patterns

### Step 2: Ensure Test Coverage
- Write tests before refactoring (if missing)
- Tests should pass before AND after
- Use tests as a safety net

### Step 3: Make Small Changes
- One refactoring at a time
- Commit after each successful change
- Keep changes reversible

### Step 4: Verify Behavior
- Run all tests after each change
- Check for regressions
- Validate performance if relevant

### Step 5: Clean Up
- Remove dead code
- Update documentation
- Review for additional improvements

## Complexity Metrics

### When to Refactor

| Metric | Threshold | Action |
|--------|-----------|--------|
| **Cyclomatic Complexity** | > 10 | Extract methods |
| **Lines per Method** | > 30 | Extract methods |
| **Lines per Class** | > 300 | Extract classes |
| **Parameters** | > 4 | Parameter object |
| **Nesting Depth** | > 3 | Early returns, extract |
| **Duplicate Code** | > 3 occurrences | Extract to shared |

### Measuring Complexity

```python
# Tools for measuring
# Python: radon, flake8-cognitive-complexity
# JavaScript: eslint-plugin-complexity
# General: SonarQube, CodeClimate

# Example: radon output
# M 42:0 process_order - C (13)  # Complexity 13 = needs refactoring
# M 78:0 validate_input - A (3)   # Complexity 3 = good

# Run with: radon cc src/ -a -s
```

## Refactoring Checklist

Before refactoring:
- [ ] Tests exist and pass
- [ ] Code is version controlled
- [ ] Understand the current behavior
- [ ] Identified specific smell to fix

During refactoring:
- [ ] Make one change at a time
- [ ] Run tests after each change
- [ ] Commit frequently
- [ ] Keep behavior identical

After refactoring:
- [ ] All tests still pass
- [ ] Code is more readable
- [ ] Complexity is reduced
- [ ] Documentation updated
- [ ] No new bugs introduced

## Output Format

When suggesting refactorings, provide:

```
## Refactoring Analysis

### Code Smell Identified
**Type:** [Smell name]
**Severity:** [High/Medium/Low]
**Location:** `file.py:42-78`

### Current Code Issues
1. [Issue 1]
2. [Issue 2]
3. [Issue 3]

### Recommended Refactoring
**Pattern:** [Refactoring pattern name]

**Current Code:**
```python
[Code before refactoring]
```

**Refactored Code:**
```python
[Code after refactoring]
```

### Benefits
- [Benefit 1]
- [Benefit 2]
- [Benefit 3]

### Migration Steps
1. [Step 1]
2. [Step 2]
3. [Step 3]

### Testing Considerations
- [What tests to add/modify]
```

## Notes

- Always refactor with tests as a safety net
- Prefer many small refactorings over big rewrites
- Don't mix refactoring with adding new features
- Refactor incrementally, not all at once
- Sometimes "good enough" code is acceptable
- Consider team readability over clever solutions
- Document significant design decisions

