--- name: refactor-legacy-code description: Modernize and improve legacy codebases while maintaining functionality. Use when you need to refactor old code, reduce technical debt, modernize deprecated patterns, or improve code maintainability without breaking existing behavior. --- # Refactor Legacy Code ## Overview This skill helps you systematically refactor legacy code to improve maintainability, readability, and performance while preserving existing functionality. It follows industry best practices for safe refactoring with comprehensive testing. ## When to Use - Modernizing outdated code patterns or deprecated APIs - Reducing technical debt in existing codebases - Improving code readability and maintainability - Extracting reusable components from monolithic code - Upgrading to newer language features or frameworks - Preparing code for new feature development ## Instructions ### 1. **Code Assessment** First, analyze the legacy code to understand: ```bash # Review the codebase structure tree -L 3 -I 'node_modules|dist|build' # Check for outdated dependencies npm outdated # or pip list --outdated, composer outdated, etc. # Identify code complexity hotspots # Use tools like: # - SonarQube for code smells # - eslint for JavaScript # - pylint for Python # - RuboCop for Ruby ``` **Assessment Checklist:** - [ ] Identify deprecated patterns and APIs - [ ] Locate tightly coupled components - [ ] Find duplicated code blocks - [ ] Review test coverage gaps - [ ] Document current behavior and edge cases - [ ] Identify performance bottlenecks ### 2. **Establish Safety Net** Before refactoring, ensure you have comprehensive tests: ```javascript // Add characterization tests to lock in current behavior describe('LegacyFeature', () => { it('should preserve existing behavior during refactoring', () => { // Test current implementation behavior const input = { /* realistic test data */ }; const result = legacyFunction(input); // Document expected output expect(result).toEqual({ /* current actual output */ }); }); }); ``` **Testing Strategy:** - Add unit tests for critical paths - Create integration tests for component interactions - Document edge cases and error scenarios - Set up test coverage monitoring - Run tests before each refactoring step ### 3. **Incremental Refactoring** Apply refactoring patterns systematically: #### Extract Function/Method ```javascript // BEFORE: Long, complex function function processUserData(user) { // 50 lines of mixed validation, transformation, and business logic if (!user.email || !user.email.includes('@')) return null; const normalized = user.email.toLowerCase().trim(); // ... more complex logic } // AFTER: Extracted, focused functions function validateEmail(email) { return email && email.includes('@'); } function normalizeEmail(email) { return email.toLowerCase().trim(); } function processUserData(user) { if (!validateEmail(user.email)) return null; const email = normalizeEmail(user.email); // Clear, readable flow } ``` #### Replace Conditionals with Polymorphism ```python # BEFORE: Complex conditional logic def calculate_price(customer_type, base_price): if customer_type == 'regular': return base_price elif customer_type == 'premium': return base_price * 0.9 elif customer_type == 'vip': return base_price * 0.8 else: return base_price # AFTER: Polymorphic approach class PricingStrategy: def calculate(self, base_price): return base_price class RegularPricing(PricingStrategy): pass class PremiumPricing(PricingStrategy): def calculate(self, base_price): return base_price * 0.9 class VIPPricing(PricingStrategy): def calculate(self, base_price): return base_price * 0.8 # Usage pricing = pricing_strategies[customer_type] price = pricing.calculate(base_price) ``` #### Introduce Parameter Object ```typescript // BEFORE: Long parameter lists function createUser( firstName: string, lastName: string, email: string, phone: string, address: string, city: string, state: string, zip: string ) { // ... } // AFTER: Parameter object interface UserData { firstName: string; lastName: string; email: string; phone: string; address: Address; } interface Address { street: string; city: string; state: string; zip: string; } function createUser(userData: UserData) { // ... } ``` ### 4. **Modernize Patterns** Replace outdated patterns with modern equivalents: #### Promises over Callbacks ```javascript // BEFORE: Callback hell function fetchUserData(userId, callback) { db.query('SELECT * FROM users WHERE id = ?', [userId], (err, user) => { if (err) return callback(err); db.query('SELECT * FROM orders WHERE user_id = ?', [userId], (err, orders) => { if (err) return callback(err); callback(null, { user, orders }); }); }); } // AFTER: Async/await async function fetchUserData(userId) { const user = await db.query('SELECT * FROM users WHERE id = ?', [userId]); const orders = await db.query('SELECT * FROM orders WHERE user_id = ?', [userId]); return { user, orders }; } ``` #### Modern Language Features ```javascript // BEFORE: var and string concatenation var userName = user.firstName + ' ' + user.lastName; var isActive = user.status === 'active' ? true : false; // AFTER: const/let and template literals const userName = `${user.firstName} ${user.lastName}`; const isActive = user.status === 'active'; ``` ### 5. **Reduce Dependencies** Break tight coupling: ```python # BEFORE: Tight coupling to specific implementation class OrderProcessor: def __init__(self): self.db = MySQLDatabase() # Tightly coupled self.email = SendGridEmail() # Tightly coupled def process_order(self, order): self.db.save(order) self.email.send(order.customer_email, "Order confirmed") # AFTER: Dependency injection class OrderProcessor: def __init__(self, database, email_service): self.db = database # Any database implementation self.email = email_service # Any email service def process_order(self, order): self.db.save(order) self.email.send(order.customer_email, "Order confirmed") # Easy to test with mocks processor = OrderProcessor(MockDatabase(), MockEmailService()) ``` ### 6. **Documentation** Document refactoring decisions: ```markdown ## Refactoring Log ### 2025-01-15: Extract Payment Processing **Rationale**: Payment logic was embedded in order controller (500 lines) **Changes**: - Extracted PaymentService with single responsibility - Introduced PaymentGateway interface for flexibility - Added comprehensive unit tests (95% coverage) **Breaking Changes**: None (internal refactoring only) **Performance Impact**: 15% improvement in order processing time ### 2025-01-16: Replace Callback with Async/Await **Rationale**: Callback hell in user authentication flow **Changes**: - Converted all authentication methods to async/await - Simplified error handling with try/catch - Improved readability (reduced from 150 to 80 lines) **Breaking Changes**: Function signatures changed (requires updates in calling code) **Migration**: Updated all 12 call sites in controllers ``` ## Best Practices ### ✅ DO - **Refactor incrementally**: Small, testable changes - **Run tests frequently**: After each refactoring step - **Commit often**: Create logical, atomic commits - **Keep existing tests passing**: Don't break functionality - **Use IDE refactoring tools**: Safer than manual edits - **Review code coverage**: Ensure tests cover refactored code - **Document decisions**: Why, not just what - **Seek peer review**: Fresh eyes catch issues ### ❌ DON'T - **Mix refactoring with new features**: Separate concerns - **Refactor without tests**: Recipe for breaking changes - **Change behavior**: Refactoring should preserve functionality - **Refactor large chunks**: Increases risk and review difficulty - **Ignore code smells**: Address them systematically - **Skip documentation**: Future maintainers need context ## Common Pitfalls ### 1. **Over-Engineering** ```javascript // ❌ Too complex for simple case class UserNameFormatterFactory { createFormatter(type) { return new UserNameFormatter(new FormattingStrategy(type)); } } // ✅ Appropriate for simple case function formatUserName(firstName, lastName) { return `${firstName} ${lastName}`; } ``` ### 2. **Premature Optimization** Focus on readability first, then optimize bottlenecks identified by profiling. ### 3. **Breaking Backward Compatibility** Use deprecation warnings before removing public APIs: ```typescript /** @deprecated Use createUser(userData) instead. Will be removed in v2.0 */ function createUserOld(firstName: string, lastName: string, email: string) { console.warn('createUserOld is deprecated. Use createUser(userData)'); return createUser({ firstName, lastName, email }); } ``` ## Testing Strategy ### Unit Tests ```javascript describe('Refactored User Service', () => { describe('validateEmail', () => { it('should accept valid email formats', () => { expect(validateEmail('user@example.com')).toBe(true); }); it('should reject invalid email formats', () => { expect(validateEmail('invalid')).toBe(false); expect(validateEmail('')).toBe(false); expect(validateEmail(null)).toBe(false); }); }); }); ``` ### Integration Tests ```python def test_refactored_order_processing(): """Ensure refactored code maintains end-to-end behavior""" # Arrange order = create_test_order() processor = OrderProcessor(test_database, test_email_service) # Act result = processor.process_order(order) # Assert assert result.status == 'completed' assert test_database.orders.count() == 1 assert test_email_service.sent_count == 1 ``` ### Regression Tests Run full test suite to ensure no unintended side effects. ## Refactoring Patterns Reference ### Common Patterns 1. **Extract Method/Function**: Break long functions into smaller ones 2. **Extract Class**: Group related functionality 3. **Inline Method**: Remove unnecessary indirection 4. **Move Method**: Place method in appropriate class 5. **Rename**: Use descriptive names 6. **Replace Magic Numbers**: Use named constants 7. **Replace Conditional with Polymorphism**: Use inheritance 8. **Introduce Parameter Object**: Group related parameters 9. **Remove Duplication**: DRY principle 10. **Simplify Conditional Logic**: Reduce complexity ## Tools & Resources ### Static Analysis Tools - **JavaScript/TypeScript**: ESLint, TSLint, SonarQube - **Python**: Pylint, Flake8, Bandit - **Java**: SonarQube, PMD, Checkstyle - **Ruby**: RuboCop, Reek - **PHP**: PHPStan, Psalm ### IDE Refactoring Support - **VS Code**: Built-in refactoring commands - **JetBrains IDEs**: Comprehensive refactoring tools - **Eclipse**: Automated refactorings - **Vim/Neovim**: Language server refactoring actions ### Recommended Reading - "Refactoring" by Martin Fowler - "Working Effectively with Legacy Code" by Michael Feathers - "Clean Code" by Robert C. Martin ## Examples ### Complete Refactoring Example #### Before ```javascript // legacy-user-service.js - 200 lines of complex, coupled code var UserService = { createUser: function(fn, ln, em, ph, addr) { if (!em || em.indexOf('@') === -1) { return { error: 'Invalid email' }; } var conn = mysql.createConnection(config); conn.connect(); conn.query( 'INSERT INTO users (first_name, last_name, email, phone, address) VALUES (?, ?, ?, ?, ?)', [fn, ln, em.toLowerCase(), ph, addr], function(err, result) { if (err) { console.log(err); return { error: 'Database error' }; } // Send welcome email var nodemailer = require('nodemailer'); var transporter = nodemailer.createTransport(emailConfig); transporter.sendMail({ to: em, subject: 'Welcome!', html: '