| name | code-refactoring |
| description | Code refactoring patterns and techniques for improving code quality without changing behavior. Use for cleaning up legacy code, reducing complexity, or improving maintainability. |
| source | https://github.com/Ethanon/developer.ai |
| license | MIT |
Code Refactoring
Refactoring Principles
When to Refactor
- Before adding new features (make change easy, then make easy change)
- After getting tests passing (red-green-refactor)
- When you see code smells
- During code review feedback
When NOT to Refactor
- Without tests covering the code
- Under tight deadlines with no safety net
- Code that will be replaced soon
- When you don't understand what the code does
Common Code Smells
Long Methods
def process_order(order: Order) -> None:
def process_order(order: Order) -> None:
validate_order(order)
total = calculate_total(order)
save_order(order, total)
notify_customer(order)
Deeply Nested Conditionals
def get_discount(user: User, order: Order) -> float:
if user:
if user.is_premium:
if order.total > 100:
if len(order.items) > 5:
return 0.2
return 0.0
def get_discount(user: User, order: Order) -> float:
if not user:
return 0.0
if not user.is_premium:
return 0.0
if order.total <= 100:
return 0.0
if len(order.items) <= 5:
return 0.0
return 0.2
Primitive Obsession
def create_user(name: str, email: str, phone: str) -> User:
if '@' not in email:
raise ValueError('Invalid email')
from dataclasses import dataclass
@dataclass(frozen=True)
class Email:
value: str
def __post_init__(self) -> None:
if '@' not in self.value:
raise ValueError('Invalid email')
def __str__(self) -> str:
return self.value
def create_user(name: str, email: Email, phone: Phone) -> User:
...
Feature Envy
def calculate_shipping(order: Order) -> float:
address = order.customer.address
weight = sum(item.weight for item in order.items)
distance = calculate_distance(address.zip_code)
return weight * distance * 0.01
class Order:
def calculate_shipping(self) -> float:
return self.total_weight * self.customer.shipping_distance * 0.01
Refactoring Techniques
Extract Method
def print_report(data: ReportData) -> None:
_print_header(data)
def _print_header(data: ReportData) -> None:
header = f"Report: {data.title}\nDate: {data.date}\n{'=' * 40}"
print(header)
Replace Conditional with Polymorphism
def get_area(shape: Shape) -> float:
if shape.type == 'circle':
return math.pi * shape.radius ** 2
elif shape.type == 'rectangle':
return shape.width * shape.height
elif shape.type == 'triangle':
return shape.base * shape.height / 2
raise ValueError(f'Unknown shape: {shape.type}')
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def get_area(self) -> float: ...
class Circle(Shape):
def __init__(self, radius: float) -> None:
self.radius = radius
def get_area(self) -> float:
return math.pi * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width: float, height: float) -> None:
self.width = width
self.height = height
def get_area(self) -> float:
return self.width * self.height
Introduce Parameter Object
def search_products(
query: str,
min_price: float,
max_price: float,
category: str,
in_stock: bool,
sort_by: str,
sort_order: str,
) -> list[Product]: ...
from dataclasses import dataclass, field
@dataclass
class SearchParams:
query: str
price_range: tuple[float, float] = (0.0, float('inf'))
category: str | None = None
in_stock: bool | None = None
sort_by: str = 'relevance'
sort_order: str = 'asc'
def search_products(params: SearchParams) -> list[Product]: ...
Replace Magic Numbers with Constants
if user.age >= 18 and order.total >= 50:
apply_discount(order, 0.1)
MINIMUM_AGE = 18
DISCOUNT_THRESHOLD = 50.0
STANDARD_DISCOUNT = 0.1
if user.age >= MINIMUM_AGE and order.total >= DISCOUNT_THRESHOLD:
apply_discount(order, STANDARD_DISCOUNT)
Safe Refactoring Process
- Ensure tests exist - Write tests if they don't (
pytest to confirm they pass)
- Make small changes - One refactoring at a time
- Run tests after each change -
pytest to catch regressions immediately
- Commit frequently - Easy to revert if something breaks
- Review the diff - Make sure behavior hasn't changed
Python-Specific Notes
- Use
mypy or pyright for type checking after refactoring — rename regressions show up immediately.
ruff can automate many simple refactors (unused imports, simplifiable conditionals).
- Prefer
@dataclass(frozen=True) for value objects over mutable classes.
- Use
Protocol instead of ABC when the interface is structural (duck typing): callers don't need to import or inherit from the protocol.
functools.lru_cache / functools.cache can replace memoization boilerplate.
Refactoring Checklist