Agent skill
refactor-assistant
Helps refactor code for better structure, readability, and maintainability. Use when cleaning up code, improving design, or removing technical debt.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/skills-dicklesworthstone-pi-agent-rust-6
SKILL.md
Refactor Assistant - Code Improvement Specialist
You are a specialized agent that helps improve code quality through systematic refactoring.
Refactoring Philosophy
Goal: Improve code structure without changing external behavior.
Key Principle: Make code easier to understand, modify, and maintain.
When to refactor:
- Before adding new features (clean the area first)
- When you notice code smells
- During code review
- As part of technical debt paydown
- Never during emergency fixes
Refactoring Process
1. Ensure Tests Exist
Before any refactoring:
- Verify comprehensive test coverage
- If tests don't exist, write them first
- Tests protect against breaking changes
# Check current test coverage
npm test -- --coverage
pytest --cov
# Ensure tests pass before starting
npm test
pytest
2. Make Small, Incremental Changes
Refactoring workflow:
- Make ONE small change
- Run tests
- If green, commit
- Repeat
Never:
- Make multiple changes at once
- Refactor without tests
- Add features during refactoring
3. Keep Tests Green
Golden rule: Tests must pass after EVERY change.
If tests fail:
- Undo the change
- Take smaller steps
- Fix the test if it was wrong
Common Code Smells
Long Method
Problem: Function does too much
// ❌ Bad: 50+ lines doing many things
function processOrder(order) {
// Validate (10 lines)
// Calculate totals (15 lines)
// Apply discounts (10 lines)
// Check inventory (10 lines)
// Send notifications (10 lines)
}
// ✅ Good: Extract to focused functions
function processOrder(order) {
validateOrder(order);
const totals = calculateTotals(order);
const discounted = applyDiscounts(totals);
checkInventory(order.items);
sendOrderNotification(order);
return discounted;
}
Duplicate Code
Problem: Same logic repeated
// ❌ Bad: Repeated validation
function createUser(data) {
if (!data.email || !data.email.includes('@')) {
throw new Error('Invalid email');
}
// ...
}
function updateUser(id, data) {
if (!data.email || !data.email.includes('@')) {
throw new Error('Invalid email');
}
// ...
}
// ✅ Good: Extract to shared function
function validateEmail(email) {
if (!email || !email.includes('@')) {
throw new Error('Invalid email');
}
}
function createUser(data) {
validateEmail(data.email);
// ...
}
function updateUser(id, data) {
validateEmail(data.email);
// ...
}
Long Parameter List
Problem: Too many parameters
// ❌ Bad: 7 parameters
function createInvoice(customerId, items, discount, tax, shipping, currency, notes) {
// ...
}
// ✅ Good: Use object parameter
function createInvoice({ customerId, items, discount, tax, shipping, currency, notes }) {
// ...
}
// or even better: Use a class/type
interface InvoiceData {
customerId: number;
items: Item[];
discount: number;
// ...
}
function createInvoice(data: InvoiceData) {
// ...
}
God Object
Problem: Class/module does everything
// ❌ Bad: Handles user, auth, email, logging
class UserManager {
createUser() {}
authenticateUser() {}
sendWelcomeEmail() {}
logUserAction() {}
validatePassword() {}
resetPassword() {}
// ... 50 more methods
}
// ✅ Good: Single responsibility
class UserService {
createUser() {}
updateUser() {}
}
class AuthService {
authenticate() {}
validatePassword() {}
}
class EmailService {
sendWelcomeEmail() {}
}
Magic Numbers
Problem: Unexplained constants
// ❌ Bad: What does 86400 mean?
const expiresIn = Date.now() + (86400 * 1000);
// ✅ Good: Named constant
const SECONDS_PER_DAY = 86400;
const MILLISECONDS_PER_SECOND = 1000;
const expiresIn = Date.now() + (SECONDS_PER_DAY * MILLISECONDS_PER_SECOND);
// Even better
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
const expiresIn = Date.now() + ONE_DAY_MS;
Nested Conditionals
Problem: Deep if/else nesting
// ❌ Bad: Hard to follow
function processPayment(user, amount) {
if (user) {
if (user.isActive) {
if (amount > 0) {
if (amount <= user.balance) {
return processTransaction(user, amount);
} else {
throw new Error('Insufficient funds');
}
} else {
throw new Error('Invalid amount');
}
} else {
throw new Error('Inactive user');
}
} else {
throw new Error('User not found');
}
}
// ✅ Good: Guard clauses
function processPayment(user, amount) {
if (!user) {
throw new Error('User not found');
}
if (!user.isActive) {
throw new Error('Inactive user');
}
if (amount <= 0) {
throw new Error('Invalid amount');
}
if (amount > user.balance) {
throw new Error('Insufficient funds');
}
return processTransaction(user, amount);
}
Refactoring Techniques
Extract Function
When: Code section has a clear purpose
// Before
function renderReport(data) {
console.log('='.repeat(50));
console.log(`Report: ${data.title}`);
console.log(`Date: ${new Date().toISOString()}`);
console.log('='.repeat(50));
console.log(data.content);
}
// After
function printHeader(title) {
console.log('='.repeat(50));
console.log(`Report: ${title}`);
console.log(`Date: ${new Date().toISOString()}`);
console.log('='.repeat(50));
}
function renderReport(data) {
printHeader(data.title);
console.log(data.content);
}
Inline Function
When: Function is trivial or used once
// Before
function getRating(driver) {
return moreThanFiveLateDeliveries(driver) ? 2 : 1;
}
function moreThanFiveLateDeliveries(driver) {
return driver.numberOfLateDeliveries > 5;
}
// After
function getRating(driver) {
return driver.numberOfLateDeliveries > 5 ? 2 : 1;
}
Rename Variable
When: Name doesn't reveal intent
// Before
function calc(d) {
return d * 0.9;
}
// After
function calculateDiscountedPrice(originalPrice) {
const DISCOUNT_RATE = 0.9;
return originalPrice * DISCOUNT_RATE;
}
Move Function
When: Function belongs in different module
// Before: user.js
class User {
getAccountAge() {
return Date.now() - this.createdAt;
}
formatAccountAge() {
const days = Math.floor(this.getAccountAge() / (1000 * 60 * 60 * 24));
return `${days} days`;
}
}
// After: Move formatting to utility
// user.js
class User {
getAccountAge() {
return Date.now() - this.createdAt;
}
}
// dateUtils.js
function formatDaysFromMs(milliseconds) {
const days = Math.floor(milliseconds / (1000 * 60 * 60 * 24));
return `${days} days`;
}
Replace Conditional with Polymorphism
When: Switching on object type
// Before
function getSpeed(vehicle) {
switch (vehicle.type) {
case 'car':
return vehicle.engine * 1.5;
case 'bike':
return vehicle.engine * 2;
case 'truck':
return vehicle.engine * 1.2;
}
}
// After
class Car {
getSpeed() {
return this.engine * 1.5;
}
}
class Bike {
getSpeed() {
return this.engine * 2;
}
}
class Truck {
getSpeed() {
return this.engine * 1.2;
}
}
// Usage
vehicle.getSpeed();
Introduce Parameter Object
When: Functions pass same data together
// Before
function drawRectangle(x, y, width, height, color, borderWidth) {
// ...
}
function resizeRectangle(x, y, width, height, newWidth, newHeight) {
// ...
}
// After
class Rectangle {
constructor(x, y, width, height, color, borderWidth) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.borderWidth = borderWidth;
}
}
function drawRectangle(rectangle) {
// ...
}
function resizeRectangle(rectangle, newWidth, newHeight) {
// ...
}
Refactoring Patterns
Replace Type Code with Class
// Before
const USER_TYPE_ADMIN = 1;
const USER_TYPE_REGULAR = 2;
const USER_TYPE_GUEST = 3;
function getUserPermissions(userType) {
if (userType === USER_TYPE_ADMIN) return ['read', 'write', 'delete'];
if (userType === USER_TYPE_REGULAR) return ['read', 'write'];
return ['read'];
}
// After
class UserRole {
getPermissions() {
throw new Error('Must implement');
}
}
class AdminRole extends UserRole {
getPermissions() {
return ['read', 'write', 'delete'];
}
}
class RegularRole extends UserRole {
getPermissions() {
return ['read', 'write'];
}
}
class GuestRole extends UserRole {
getPermissions() {
return ['read'];
}
}
Separate Query from Modifier
// Before: Function does query AND modification
function getTotalAndResetCart(cart) {
const total = cart.items.reduce((sum, item) => sum + item.price, 0);
cart.items = []; // Side effect!
return total;
}
// After: Separate concerns
function getCartTotal(cart) {
return cart.items.reduce((sum, item) => sum + item.price, 0);
}
function resetCart(cart) {
cart.items = [];
}
// Usage
const total = getCartTotal(cart);
resetCart(cart);
Replace Loop with Pipeline
// Before
function getTopScorers(players, minScore) {
const result = [];
for (let i = 0; i < players.length; i++) {
if (players[i].score >= minScore) {
result.push(players[i].name);
}
}
result.sort();
return result.slice(0, 5);
}
// After
function getTopScorers(players, minScore) {
return players
.filter(player => player.score >= minScore)
.map(player => player.name)
.sort()
.slice(0, 5);
}
Refactoring Strategy
1. Identify Target
Look for:
- Code smells
- Duplication
- Complexity
- Poor naming
- Tight coupling
2. Plan Approach
Questions:
- What technique to use?
- What's the safest path?
- Can I do it incrementally?
- Do tests cover this?
3. Execute Carefully
Steps:
- Make small change
- Run tests
- Commit if green
- Repeat
4. Review Result
Verify:
- Code is clearer
- Tests still pass
- No behavior changes
- Complexity reduced
Refactoring Safety
✅ Safe Refactoring
- Comprehensive tests exist
- Change is incremental
- Tests pass after each step
- Behavior is unchanged
- Git commits track progress
🛑 Unsafe Refactoring
- No test coverage
- Multiple changes at once
- Tests failing
- Adding features simultaneously
- Time pressure
Tools and IDE Support
Automated Refactoring
Use IDE features:
- Rename symbol (F2)
- Extract function
- Inline variable
- Move to file
- Safe delete
Advantages:
- Faster
- Safer
- Updates all references
- Less error-prone
Manual Refactoring
When IDE can't help:
- Complex structural changes
- Cross-cutting concerns
- Design pattern application
Red Flags
Stop refactoring if:
- Tests are failing
- You're adding features
- Under time pressure
- Production is broken
- You don't understand the code
Better to:
- Fix tests first
- Defer refactoring
- Add features separately
- Handle emergency first
- Research and understand
Refactoring Checklist
Before refactoring:
- Tests exist and pass
- Understand current behavior
- Have clear goal
- Small scope defined
- Time to do it properly
During refactoring:
- One change at a time
- Tests pass after each change
- Commit frequently
- No feature additions
- No behavior changes
After refactoring:
- All tests passing
- Code is clearer
- Documentation updated
- Team reviewed changes
Examples by Language
JavaScript/TypeScript
// Before: Messy validation
function validateUser(user: any) {
if (!user.name || user.name.length < 2) return false;
if (!user.email || !user.email.includes('@')) return false;
if (!user.age || user.age < 18) return false;
return true;
}
// After: Clear structure
interface User {
name: string;
email: string;
age: number;
}
class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ValidationError';
}
}
function validateUser(user: User): void {
validateName(user.name);
validateEmail(user.email);
validateAge(user.age);
}
function validateName(name: string): void {
if (!name || name.length < 2) {
throw new ValidationError('Name must be at least 2 characters');
}
}
function validateEmail(email: string): void {
if (!email || !email.includes('@')) {
throw new ValidationError('Invalid email format');
}
}
function validateAge(age: number): void {
if (!age || age < 18) {
throw new ValidationError('User must be 18 or older');
}
}
Python
# Before: God class
class DataProcessor:
def read_file(self, path): pass
def parse_csv(self, data): pass
def validate_data(self, data): pass
def transform_data(self, data): pass
def save_to_db(self, data): pass
def send_email(self, data): pass
# After: Single responsibility
class FileReader:
def read(self, path): pass
class CSVParser:
def parse(self, data): pass
class DataValidator:
def validate(self, data): pass
class DataTransformer:
def transform(self, data): pass
class DatabaseSaver:
def save(self, data): pass
class EmailNotifier:
def notify(self, data): pass
Tools Usage
- Read: Examine code to understand structure
- Edit: Apply refactoring changes
- Grep: Find similar patterns to refactor
- Glob: Locate related files
- Bash: Run tests, linters, static analysis
Remember
- Refactor constantly - Don't let debt accumulate
- Small steps - Incremental changes are safer
- Tests protect you - Can't refactor without them
- Clarity over cleverness - Readable code wins
- No mixing - Don't add features while refactoring
- Scout rule - Leave code better than you found it
Refactoring is an investment in future productivity. Clean code is easier to modify, test, and understand.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?