257 lines
5.1 KiB
Markdown
257 lines
5.1 KiB
Markdown
---
|
|
name: refactoring-guide
|
|
description: Practical refactoring techniques and when to apply them. Auto-loaded when discussing code improvements or refactoring.
|
|
---
|
|
|
|
# Refactoring Guide
|
|
|
|
## When to Refactor
|
|
|
|
### Red Flags
|
|
- Functions longer than 50 lines
|
|
- Classes with 10+ methods
|
|
- Nested conditionals 3+ levels deep
|
|
- Duplicated code in 3+ places
|
|
- Complex boolean conditions
|
|
- High cyclomatic complexity (>10)
|
|
|
|
## Common Refactorings
|
|
|
|
### Extract Function
|
|
**Before:**
|
|
```javascript
|
|
function processOrder(order) {
|
|
// Calculate total
|
|
let total = 0;
|
|
for (let item of order.items) {
|
|
total += item.price * item.quantity;
|
|
}
|
|
|
|
// Apply discount
|
|
if (order.customer.isPremium) {
|
|
total *= 0.9;
|
|
}
|
|
|
|
return total;
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```javascript
|
|
function processOrder(order) {
|
|
const subtotal = calculateSubtotal(order.items);
|
|
return applyDiscount(subtotal, order.customer);
|
|
}
|
|
|
|
function calculateSubtotal(items) {
|
|
return items.reduce((sum, item) =>
|
|
sum + item.price * item.quantity, 0);
|
|
}
|
|
|
|
function applyDiscount(amount, customer) {
|
|
return customer.isPremium ? amount * 0.9 : amount;
|
|
}
|
|
```
|
|
|
|
### Replace Conditional with Polymorphism
|
|
**Before:**
|
|
```python
|
|
def calculate_pay(employee):
|
|
if employee.type == "manager":
|
|
return employee.salary + employee.bonus
|
|
elif employee.type == "engineer":
|
|
return employee.salary + employee.stock_options
|
|
else:
|
|
return employee.salary
|
|
```
|
|
|
|
**After:**
|
|
```python
|
|
class Employee:
|
|
def calculate_pay(self):
|
|
return self.salary
|
|
|
|
class Manager(Employee):
|
|
def calculate_pay(self):
|
|
return self.salary + self.bonus
|
|
|
|
class Engineer(Employee):
|
|
def calculate_pay(self):
|
|
return self.salary + self.stock_options
|
|
```
|
|
|
|
### Extract Constant
|
|
**Before:**
|
|
```javascript
|
|
if (age >= 18 && age <= 65) {
|
|
// eligible
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```javascript
|
|
const MIN_ELIGIBLE_AGE = 18;
|
|
const MAX_ELIGIBLE_AGE = 65;
|
|
|
|
if (age >= MIN_ELIGIBLE_AGE && age <= MAX_ELIGIBLE_AGE) {
|
|
// eligible
|
|
}
|
|
```
|
|
|
|
### Introduce Parameter Object
|
|
**Before:**
|
|
```typescript
|
|
function createUser(
|
|
name: string,
|
|
email: string,
|
|
age: number,
|
|
address: string,
|
|
phone: string
|
|
) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```typescript
|
|
interface UserData {
|
|
name: string;
|
|
email: string;
|
|
age: number;
|
|
address: string;
|
|
phone: string;
|
|
}
|
|
|
|
function createUser(userData: UserData) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Replace Magic Number with Named Constant
|
|
**Before:**
|
|
```go
|
|
func calculateDiscount(price float64) float64 {
|
|
return price * 0.15
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```go
|
|
const LOYALTY_DISCOUNT_RATE = 0.15
|
|
|
|
func calculateDiscount(price float64) float64 {
|
|
return price * LOYALTY_DISCOUNT_RATE
|
|
}
|
|
```
|
|
|
|
### Decompose Conditional
|
|
**Before:**
|
|
```javascript
|
|
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
|
|
charge = quantity * winterRate + winterServiceCharge;
|
|
} else {
|
|
charge = quantity * summerRate;
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```javascript
|
|
if (isWinter(date)) {
|
|
charge = winterCharge(quantity);
|
|
} else {
|
|
charge = summerCharge(quantity);
|
|
}
|
|
|
|
function isWinter(date) {
|
|
return date.before(SUMMER_START) || date.after(SUMMER_END);
|
|
}
|
|
|
|
function winterCharge(quantity) {
|
|
return quantity * winterRate + winterServiceCharge;
|
|
}
|
|
|
|
function summerCharge(quantity) {
|
|
return quantity * summerRate;
|
|
}
|
|
```
|
|
|
|
### Replace Nested Conditional with Guard Clauses
|
|
**Before:**
|
|
```python
|
|
def get_payment(employee):
|
|
if employee.is_active:
|
|
if employee.has_salary:
|
|
return employee.salary
|
|
else:
|
|
return 0
|
|
else:
|
|
return 0
|
|
```
|
|
|
|
**After:**
|
|
```python
|
|
def get_payment(employee):
|
|
if not employee.is_active:
|
|
return 0
|
|
if not employee.has_salary:
|
|
return 0
|
|
return employee.salary
|
|
```
|
|
|
|
## Large-Scale Refactorings
|
|
|
|
### Split Module
|
|
When a file is too large (500+ lines), split by responsibility:
|
|
```
|
|
user.ts (1000 lines)
|
|
↓
|
|
user-model.ts (200 lines)
|
|
user-service.ts (300 lines)
|
|
user-validator.ts (150 lines)
|
|
user-utils.ts (100 lines)
|
|
```
|
|
|
|
### Extract Service Layer
|
|
Move business logic from controllers:
|
|
```
|
|
Before: Controller has 500 lines with business logic
|
|
After:
|
|
- Controller: 100 lines (request handling)
|
|
- Service: 400 lines (business logic)
|
|
```
|
|
|
|
### Introduce Repository
|
|
Abstract database access:
|
|
```
|
|
Before: Direct SQL/ORM calls throughout code
|
|
After: Repository classes handle all data access
|
|
```
|
|
|
|
## Refactoring Workflow
|
|
|
|
1. **Ensure tests exist** (or write them first)
|
|
2. **Make small changes** (one refactoring at a time)
|
|
3. **Run tests** after each change
|
|
4. **Commit frequently** with clear messages
|
|
5. **Don't mix refactoring with feature work**
|
|
|
|
## Red Flags to Watch For
|
|
|
|
- **Premature optimization**: Don't refactor for performance without profiling
|
|
- **Over-engineering**: Don't add complexity for theoretical future needs
|
|
- **Breaking changes**: Be careful with public APIs
|
|
- **Scope creep**: Stick to planned refactoring, don't expand mid-work
|
|
|
|
## Quick Wins
|
|
|
|
### Low-hanging fruit for immediate improvement:
|
|
1. Rename unclear variables
|
|
2. Extract magic numbers to constants
|
|
3. Remove commented-out code
|
|
4. Fix inconsistent formatting
|
|
5. Add missing error handling
|
|
6. Remove unused imports/functions
|
|
7. Simplify boolean expressions
|
|
|
|
These refactorings make code more maintainable and easier to understand.
|