Refactoring Skill
Apply systematic refactoring techniques to improve code quality, maintainability, and readability without changing external behavior.
When To Use
- •Code is working but hard to understand
- •Duplicated code across files
- •Functions too long or complex
- •Poor naming
- •Tight coupling
- •Before adding new features to messy code
Core Principles
- •Preserve behavior - Tests should still pass
- •Small steps - Refactor incrementally
- •Test first - Ensure tests exist before refactoring
- •One thing at a time - Don't mix refactoring with features
- •Commit often - Small, focused commits
Common Refactorings
Extract Function
When: Function too long, repeated code, or complex logic
javascript
// Before
function processOrder(order) {
// validate order (20 lines)
// calculate total (15 lines)
// apply discount (25 lines)
// save to database (10 lines)
}
// After
function processOrder(order) {
validateOrder(order)
const total = calculateTotal(order)
const finalTotal = applyDiscount(total, order.customerTier)
saveOrder(order, finalTotal)
}
Extract Variable
When: Complex expression hard to understand
javascript
// Before
if (user.age >= 18 && user.hasLicense && user.violations < 3) {
// allow rental
}
// After
const isEligibleToRent = user.age >= 18 &&
user.hasLicense &&
user.violations < 3
if (isEligibleToRent) {
// allow rental
}
Rename
When: Name doesn't reflect purpose
javascript
// Before
const d = new Date()
const temp = calculateValue()
function process(x) { }
// After
const currentDate = new Date()
const userBalance = calculateBalance()
function processPayment(transaction) { }
Remove Duplication
When: Same code in multiple places
javascript
// Before
function formatUserName(user) {
return user.firstName + ' ' + user.lastName
}
function displayUser(user) {
const name = user.firstName + ' ' + user.lastName
return `User: ${name}`
}
// After
function getFullName(user) {
return `${user.firstName} ${user.lastName}`
}
function formatUserName(user) {
return getFullName(user)
}
function displayUser(user) {
return `User: ${getFullName(user)}`
}
Simplify Conditional
When: Complex nested conditions
javascript
// Before
if (user) {
if (user.isActive) {
if (user.hasPermission('admin')) {
return true
}
}
}
return false
// After
return user?.isActive && user.hasPermission('admin')
Replace Magic Numbers
When: Unexplained numeric literals
javascript
// Before
if (user.loginAttempts > 5) {
lockAccount()
}
setTimeout(retry, 300000)
// After
const MAX_LOGIN_ATTEMPTS = 5
const RETRY_DELAY_MS = 5 * 60 * 1000 // 5 minutes
if (user.loginAttempts > MAX_LOGIN_ATTEMPTS) {
lockAccount()
}
setTimeout(retry, RETRY_DELAY_MS)
Refactoring Process
1. Ensure Tests Exist
Before refactoring:
- •Run tests to verify current behavior
- •Add tests if missing
- •Ensure tests are comprehensive
2. Make Small Changes
- •One refactoring at a time
- •Run tests after each change
- •Commit if tests pass
- •Revert if tests fail
3. Improve Incrementally
Don't try to perfect everything at once:
- •Start with obvious improvements
- •Focus on high-impact areas
- •Stop when good enough
Red Flags (When NOT to Refactor)
- •No tests exist (write tests first)
- •Under time pressure (schedule it properly)
- •Changing behavior (that's a feature, not refactoring)
- •Just making it "your way" (style preferences aren't refactoring)
Common Patterns
- •Long Function -> Extract Methods
- •Duplicated Code -> Extract Common Function
- •Complex Conditional -> Extract Predicate Functions
- •Magic Numbers -> Named Constants
- •Long Parameter List -> Parameter Object
- •Primitive Obsession -> Value Objects
- •Large Class -> Extract Classes
Remember
- •Refactoring is not Rewriting
- •Keep changes small and focused
- •Tests are your safety net
- •Commit often
- •Don't mix refactoring with features
- •Stop when improvement marginal