Martin Fowler's Refactoring Techniques Enhanced by AI

Introduction

Martin Fowler's "Refactoring: Improving the Design of Existing Code" has been the definitive guide for improving code quality. With AI, we can apply these techniques more effectively and discover new refactoring opportunities.

AI-Enhanced Refactoring Catalog

1. Extract Method

AI can identify code that should be extracted into separate methods and suggest the extraction.


// Before: Long method with multiple responsibilities
function processOrder(order) {
  // Validate order
  if (!order.items || order.items.length === 0) {
    throw new Error('Order must have items');
  }
  if (!order.customerId) {
    throw new Error('Order must have customer');
  }
  if (order.total <= 0) {
    throw new Error('Order total must be positive');
  }

  // Calculate discounts
  let discount = 0;
  if (order.customer.isPremium) {
    discount = order.total * 0.1;
  } else if (order.total > 100) {
    discount = order.total * 0.05;
  }

  // Apply discount
  order.finalTotal = order.total - discount;
  
  // Save order
  return orderRepository.save(order);
}

// After: AI-suggested refactoring
function processOrder(order) {
  validateOrder(order);
  applyDiscounts(order);
  return orderRepository.save(order);
}

function validateOrder(order) {
  if (!order.items || order.items.length === 0) {
    throw new Error('Order must have items');
  }
  if (!order.customerId) {
    throw new Error('Order must have customer');
  }
  if (order.total <= 0) {
    throw new Error('Order total must be positive');
  }
}

function applyDiscounts(order) {
  let discount = 0;
  if (order.customer.isPremium) {
    discount = order.total * 0.1;
  } else if (order.total > 100) {
    discount = order.total * 0.05;
  }
  order.finalTotal = order.total - discount;
}
            

2. Replace Conditional with Polymorphism

AI can identify complex conditional logic and suggest polymorphic solutions.


// Before: Complex conditional logic
function calculateShippingCost(order) {
  if (order.shippingType === 'standard') {
    return order.weight * 2.5;
  } else if (order.shippingType === 'express') {
    return order.weight * 5.0;
  } else if (order.shippingType === 'overnight') {
    return order.weight * 10.0;
  } else {
    throw new Error('Unknown shipping type');
  }
}

// After: AI-suggested polymorphic solution
abstract class ShippingCalculator {
  abstract calculateCost(weight: number): number;
}

class StandardShipping extends ShippingCalculator {
  calculateCost(weight: number): number {
    return weight * 2.5;
  }
}

class ExpressShipping extends ShippingCalculator {
  calculateCost(weight: number): number {
    return weight * 5.0;
  }
}

class OvernightShipping extends ShippingCalculator {
  calculateCost(weight: number): number {
    return weight * 10.0;
  }
}

class ShippingCalculatorFactory {
  static create(type: string): ShippingCalculator {
    switch (type) {
      case 'standard': return new StandardShipping();
      case 'express': return new ExpressShipping();
      case 'overnight': return new OvernightShipping();
      default: throw new Error('Unknown shipping type');
    }
  }
}

function calculateShippingCost(order) {
  const calculator = ShippingCalculatorFactory.create(order.shippingType);
  return calculator.calculateCost(order.weight);
}
            

3. Introduce Parameter Object

AI can identify methods with many parameters and suggest parameter objects.


// Before: Method with many parameters
function createUser(
  firstName, lastName, email, password, 
  dateOfBirth, address, phoneNumber, 
  preferences, isActive, role
) {
  // Implementation
}

// After: AI-suggested parameter object
class CreateUserRequest {
  constructor(
    public firstName: string,
    public lastName: string,
    public email: string,
    public password: string,
    public dateOfBirth: Date,
    public address: Address,
    public phoneNumber: string,
    public preferences: UserPreferences,
    public isActive: boolean = true,
    public role: UserRole = UserRole.USER
  ) {}
}

function createUser(request: CreateUserRequest) {
  // Implementation
}
            

AI-Powered Refactoring Tools

1. Code Smell Detection

AI can identify code smells that humans might miss:

  • Long Methods: Methods with too many lines of code
  • Large Classes: Classes with too many responsibilities
  • Duplicate Code: Repeated code patterns
  • Complex Conditionals: Nested if statements
  • Primitive Obsession: Using primitives instead of objects

2. Refactoring Suggestions

AI can suggest specific refactoring techniques based on code analysis:


// AI Analysis Output:
// File: UserService.js
// Issues Found:
// 1. Method 'processUserData' is 45 lines long (threshold: 20)
//    Suggestion: Extract methods 'validateUserData', 'transformUserData'
// 2. Class 'UserService' has 8 methods (threshold: 6)
//    Suggestion: Extract 'UserValidationService', 'UserTransformationService'
// 3. Duplicate validation logic in methods 'createUser', 'updateUser'
//    Suggestion: Extract common validation to 'UserValidator' class
            

Refactoring Best Practices with AI

  • Small Steps: Make small, incremental changes
  • Test After Each Change: Ensure tests pass after each refactoring
  • Review AI Suggestions: Don't blindly accept AI recommendations
  • Maintain Code Intent: Ensure refactoring preserves original functionality

Bibliography

  • Fowler, M. (2018). "Refactoring: Improving the Design of Existing Code"
  • Fowler, M. (2002). "Patterns of Enterprise Application Architecture"
  • Fowler, M. (2006). "Domain-Specific Languages"
  • Beck, K. (2003). "Test-Driven Development: By Example"

Conclusion

AI enhances Martin Fowler's refactoring techniques by providing better detection of code smells and more accurate refactoring suggestions. However, human judgment remains essential for making the final refactoring decisions.

Subscribe to AI.TDD - The New Paradigm of Software Development

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe