Agent skill

refactor-assistant

Helps refactor code for better structure, readability, and maintainability. Use when cleaning up code, improving design, or removing technical debt.

Stars 163
Forks 31

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
bash
# Check current test coverage
npm test -- --coverage
pytest --cov

# Ensure tests pass before starting
npm test
pytest

2. Make Small, Incremental Changes

Refactoring workflow:

  1. Make ONE small change
  2. Run tests
  3. If green, commit
  4. 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

javascript
// ❌ 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

javascript
// ❌ 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

javascript
// ❌ 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

javascript
// ❌ 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

javascript
// ❌ 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

javascript
// ❌ 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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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

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

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.

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results