Refactoring
Comprehensive refactoring skill for safe code restructuring, migrations, and modernization.
Core Capabilities
Safe Renaming
Rename identifiers across the codebase:
Rename workflow:
-
Find all references: Search for all usages
-
Identify scope: Module-local, package-wide, or public API
-
Check dependencies: External code that might break
-
Perform rename: Update all occurrences
-
Verify: Run tests, check imports
Search patterns:
Find all references to a function
grep -rn "function_name" --include="*.py"
Find class usages
grep -rn "ClassName" --include="*.py"
Find imports
grep -rn "from .* import.function_name" --include=".py"
Renaming considerations:
-
Update docstrings mentioning the old name
-
Update comments referencing the name
-
Update configuration files
-
Update tests
-
Consider deprecation period for public APIs
Method Extraction
Extract code into separate functions:
When to extract:
-
Code block is too long (> 20 lines)
-
Code is duplicated elsewhere
-
Code has a clear single purpose
-
Code can be tested independently
Extraction process:
-
Identify the code block to extract
-
Determine inputs (parameters)
-
Determine outputs (return values)
-
Create new function with clear name
-
Replace original code with function call
-
Add tests for new function
Before:
def process_order(order): # Validate order (candidate for extraction) if not order.items: raise ValueError("Empty order") if order.total < 0: raise ValueError("Invalid total") for item in order.items: if item.quantity <= 0: raise ValueError("Invalid quantity")
# Process payment
payment_result = gateway.charge(order.total)
return payment_result
After:
def validate_order(order: Order) -> None: """Validate order has valid items and total.""" if not order.items: raise ValueError("Empty order") if order.total < 0: raise ValueError("Invalid total") for item in order.items: if item.quantity <= 0: raise ValueError("Invalid quantity")
def process_order(order: Order) -> PaymentResult: validate_order(order) return gateway.charge(order.total)
Class Splitting
Split large classes into focused components:
When to split:
-
Class has multiple responsibilities
-
Class has > 500 lines
-
Groups of methods work on different data
-
Testing requires excessive mocking
Splitting strategies:
Extract class:
Before: God class
class OrderManager: def create_order(self): ... def validate_order(self): ... def calculate_tax(self): ... def calculate_shipping(self): ... def send_confirmation_email(self): ... def send_shipping_notification(self): ...
After: Focused classes
class OrderService: def create_order(self): ... def validate_order(self): ...
class PricingService: def calculate_tax(self): ... def calculate_shipping(self): ...
class NotificationService: def send_confirmation_email(self): ... def send_shipping_notification(self): ...
Complexity Reduction
Reduce cyclomatic complexity:
Replace conditionals with polymorphism:
Before: Complex switch
def calculate_price(product_type, base_price): if product_type == "physical": return base_price + shipping_cost elif product_type == "digital": return base_price elif product_type == "subscription": return base_price * 12 * 0.9 else: raise ValueError("Unknown type")
After: Polymorphism
class Product(ABC): @abstractmethod def calculate_price(self, base_price): ...
class PhysicalProduct(Product): def calculate_price(self, base_price): return base_price + self.shipping_cost
class DigitalProduct(Product): def calculate_price(self, base_price): return base_price
class Subscription(Product): def calculate_price(self, base_price): return base_price * 12 * 0.9
Extract guard clauses:
Before: Nested conditionals
def process(data): if data: if data.is_valid: if data.is_ready: return do_processing(data) return None
After: Guard clauses
def process(data): if not data: return None if not data.is_valid: return None if not data.is_ready: return None return do_processing(data)
Code Migration
Migrate code between patterns or versions:
Migration types:
-
Python 2 to 3
-
Sync to async
-
ORM migrations
-
API version upgrades
-
Framework migrations
Migration workflow:
-
Assess scope: What needs to change
-
Create compatibility layer: If gradual migration
-
Migrate in phases: Start with low-risk areas
-
Maintain tests: Ensure behavior preserved
-
Remove old code: Clean up after migration
Example: Sync to Async
Before: Synchronous
def fetch_user(user_id: int) -> User: response = requests.get(f"/users/{user_id}") return User.from_dict(response.json())
After: Asynchronous
async def fetch_user(user_id: int) -> User: async with aiohttp.ClientSession() as session: async with session.get(f"/users/{user_id}") as response: data = await response.json() return User.from_dict(data)
Refactoring Workflow
Safe Refactoring Process
-
Ensure test coverage: Add tests if missing
-
Run tests: Verify green baseline
-
Make small change: One refactoring at a time
-
Run tests again: Verify still green
-
Commit: Save progress
-
Repeat: Continue with next change
Pre-Refactoring Checklist
[ ] Tests exist for affected code [ ] All tests pass [ ] Change is well understood [ ] Impact scope identified [ ] Rollback plan exists
Post-Refactoring Verification
[ ] All tests still pass [ ] No new linting errors [ ] Type checking passes [ ] Functionality unchanged [ ] Performance acceptable
Common Refactorings
Remove Dead Code
Find and remove:
- Unused imports
- Unused variables
- Unreachable code
- Commented-out code
- Deprecated functions
Tools:
- vulture (Python)
- autoflake (Python)
- eslint (JavaScript)
Simplify Expressions
Before
if condition == True: return True else: return False
After
return condition
Introduce Explaining Variables
Before
if user.age >= 18 and user.country in ['US', 'CA'] and user.verified: allow_purchase()
After
is_adult = user.age >= 18 is_supported_country = user.country in ['US', 'CA'] is_verified = user.verified can_purchase = is_adult and is_supported_country and is_verified
if can_purchase: allow_purchase()
Safety Guidelines
Breaking Changes
When refactoring public APIs:
-
Add deprecation warnings first
-
Maintain backwards compatibility
-
Document migration path
-
Remove after deprecation period
Database Migrations
When refactoring data models:
-
Create migration scripts
-
Test migrations on copy of data
-
Plan for rollback
-
Consider zero-downtime migrations
Performance Impact
After refactoring:
-
Run performance benchmarks
-
Compare with baseline
-
Profile if regression found
-
Optimize if necessary
Integration
Coordinate with other skills:
-
code-quality skill: Measure improvement
-
test-coverage skill: Ensure test coverage
-
architecture-review skill: Validate structural changes
-
git-workflows skill: Proper commit messages for refactors