Systematic code improvement with Claude as your refactoring partner.
Refactoring is:
- Risky - Easy to break things
- Time-consuming - Many files to touch
- Tedious - Repetitive patterns
Claude excels at:
- Identifying refactoring opportunities
- Making consistent changes across files
- Ensuring tests still pass
- Documenting what changed
"Analyze this codebase for refactoring opportunities:
- Long functions (>50 lines)
- Duplicated code
- Deep nesting (>3 levels)
- Complex conditionals
- Large files (>500 lines)"
Claude returns prioritized list with locations and suggested fixes.
"Create a refactoring plan for [specific issue]:
- What changes are needed
- Which files are affected
- What tests need updating
- Risk assessment"
"Refactor [specific function/module] following the plan.
Ensure all tests pass after each change."
"Verify the refactoring is complete:
- All tests pass
- No functionality changed
- Code metrics improved
- Changes documented"
Before:
async function processOrder(order) {
// 50 lines of validation
if (!order.items) throw new Error('No items');
if (order.items.length === 0) throw new Error('Empty order');
for (const item of order.items) {
if (!item.productId) throw new Error('Missing product ID');
if (!item.quantity || item.quantity < 1) throw new Error('Invalid quantity');
const product = await getProduct(item.productId);
if (!product) throw new Error('Product not found');
if (product.stock < item.quantity) throw new Error('Insufficient stock');
}
// 30 lines of calculation
let subtotal = 0;
for (const item of order.items) {
const product = await getProduct(item.productId);
subtotal += product.price * item.quantity;
}
const tax = subtotal * 0.1;
const shipping = subtotal > 100 ? 0 : 10;
const total = subtotal + tax + shipping;
// 20 lines of saving
// ...
}Claude Command:
"Refactor processOrder by extracting:
- validateOrder function
- calculateTotals function
- saveOrder function"
After:
async function processOrder(order) {
await validateOrder(order);
const totals = await calculateTotals(order);
return await saveOrder(order, totals);
}
async function validateOrder(order) {
if (!order.items?.length) {
throw new ValidationError('Order must have items');
}
await Promise.all(order.items.map(validateItem));
}
async function calculateTotals(order) {
const subtotal = await calculateSubtotal(order.items);
const tax = subtotal * TAX_RATE;
const shipping = subtotal > FREE_SHIPPING_THRESHOLD ? 0 : SHIPPING_COST;
return { subtotal, tax, shipping, total: subtotal + tax + shipping };
}Before:
function calculateDiscount(customer, amount) {
if (customer.type === 'regular') {
return amount * 0.05;
} else if (customer.type === 'premium') {
return amount * 0.10;
} else if (customer.type === 'vip') {
return amount * 0.15 + 10;
} else if (customer.type === 'employee') {
return amount * 0.25;
}
return 0;
}Claude Command:
"Refactor calculateDiscount to use a strategy pattern instead of conditionals"
After:
const discountStrategies = {
regular: (amount) => amount * 0.05,
premium: (amount) => amount * 0.10,
vip: (amount) => amount * 0.15 + 10,
employee: (amount) => amount * 0.25,
default: () => 0,
};
function calculateDiscount(customer, amount) {
const strategy = discountStrategies[customer.type] || discountStrategies.default;
return strategy(amount);
}Before:
// In UserService
async function createUser(data) {
const user = new User(data);
user.createdAt = new Date();
user.updatedAt = new Date();
user.createdBy = getCurrentUser().id;
await user.save();
await sendNotification('user.created', user);
await logAudit('user.created', user);
return user;
}
// In ProductService (duplicated logic)
async function createProduct(data) {
const product = new Product(data);
product.createdAt = new Date();
product.updatedAt = new Date();
product.createdBy = getCurrentUser().id;
await product.save();
await sendNotification('product.created', product);
await logAudit('product.created', product);
return product;
}Claude Command:
"Find all duplicated create/update patterns and extract into a base service or mixin"
After:
// Base service with shared logic
class BaseService {
async create(Model, data, eventName) {
const entity = new Model(data);
entity.createdAt = new Date();
entity.updatedAt = new Date();
entity.createdBy = getCurrentUser().id;
await entity.save();
await this.notifyAndLog(eventName, entity);
return entity;
}
async notifyAndLog(event, entity) {
await Promise.all([
sendNotification(event, entity),
logAudit(event, entity)
]);
}
}
// Clean implementations
class UserService extends BaseService {
async createUser(data) {
return this.create(User, data, 'user.created');
}
}Before:
function canAccessResource(user, resource) {
if (user.role === 'admin') {
return true;
}
if (user.role === 'manager' && resource.department === user.department) {
return true;
}
if (user.role === 'employee' && resource.owner === user.id) {
return true;
}
if (resource.isPublic) {
return true;
}
if (resource.sharedWith && resource.sharedWith.includes(user.id)) {
return true;
}
return false;
}Claude Command:
"Refactor canAccessResource to use early returns and clearer logic"
After:
function canAccessResource(user, resource) {
// Public resources accessible to all
if (resource.isPublic) return true;
// Check explicit sharing
if (resource.sharedWith?.includes(user.id)) return true;
// Role-based access
return hasRoleBasedAccess(user, resource);
}
function hasRoleBasedAccess(user, resource) {
const accessRules = {
admin: () => true,
manager: () => resource.department === user.department,
employee: () => resource.owner === user.id,
};
return accessRules[user.role]?.() ?? false;
}Claude Command:
"This file is 800 lines. Analyze and suggest how to split it:
- Group related functions
- Identify distinct responsibilities
- Propose new file structure"
Example Response:
Suggested split for utils.ts (800 lines):
1. utils/string.ts (150 lines)
- formatName, slugify, truncate, capitalize
2. utils/date.ts (120 lines)
- formatDate, parseDate, getRelativeTime
3. utils/validation.ts (200 lines)
- validateEmail, validatePhone, validateUrl
4. utils/api.ts (180 lines)
- fetchWithRetry, handleApiError, buildQueryString
5. utils/dom.ts (150 lines)
- scrollToElement, copyToClipboard, downloadFile
Create utils/index.ts to re-export for backwards compatibility.
"Analyze code quality metrics for this codebase:
- Average function length
- Cyclomatic complexity
- Duplication percentage
- Test coverage
- Type coverage"
"Based on metrics, create improvement targets:
- Reduce average function length from 45 to 25 lines
- Reduce cyclomatic complexity from 12 to 8
- Reduce duplication from 15% to 5%
- Increase test coverage from 60% to 80%"
"Before refactoring, ensure test coverage:
- Add tests for [function] covering current behavior
- Run tests to confirm they pass
- Then proceed with refactoring"
"Refactor incrementally:
1. Extract one function at a time
2. Run tests after each extraction
3. Commit after each successful change"
// Safe rollout of refactored code
if (featureFlags.useNewPaymentProcessor) {
return newPaymentProcessor.process(order);
} else {
return legacyPaymentProcessor.process(order);
}"After refactoring, document:
- What changed
- Migration path for callers
- Performance impact (if any)"
❌ "Refactor this untested legacy code"
✅ "First, add characterization tests for this legacy code,
then refactor while keeping tests passing"
❌ "Refactor this and also add caching"
✅ "Refactor first, commit, then add caching in a separate commit"
❌ "Refactor the entire codebase to use the new pattern"
✅ "Refactor the user module first, verify it works,
then proceed to other modules"
"Find the top 5 functions that most need refactoring"
"Refactor [function] to reduce cyclomatic complexity"
"Apply the repository pattern to all database access code"
"Identify code smells: long methods, feature envy, data clumps"
"After refactoring, compare metrics with before:
- Lines of code
- Complexity
- Test coverage"
- Start with metrics - Know what needs improvement
- Plan before acting - Understand the scope
- Test first - Never refactor untested code
- Small steps - One change at a time
- Verify continuously - Run tests after each change
- Document changes - Help future developers
- Measure improvement - Confirm metrics improved
Golden Rule: Refactoring changes structure, not behavior. If tests fail, you broke something.
See also: Feature Development | Bug Hunting Pattern