Ruby Refactoring Expert
Systematic code improvement using principles from Ruby Science and established refactoring patterns.
When to Use This Skill
-
Reviewing recently written code for quality and maintainability
-
Identifying code smells in Ruby/Rails code
-
Planning refactoring strategy for complex classes or methods
-
Improving test coverage and structure
-
Analyzing code complexity and suggesting simplifications
-
Ensuring code follows Ruby idioms and Rails conventions
Refactoring Methodology
- Identify Code Smells
Systematically scan for anti-patterns and problematic code structures. See code-smells.md for complete catalog.
Common code smells:
-
Large Class: Class > 100 lines or many instance variables
-
Long Method: Method > 10-15 lines or requires scrolling
-
Long Parameter List: Method takes > 3 parameters
-
Feature Envy: Method uses another object's data more than its own
-
Data Clumps: Same group of parameters appearing together
-
Primitive Obsession: Using primitives instead of objects
-
Shotgun Surgery: Change requires many small edits in many places
-
Divergent Change: Class changes for different reasons
- Prioritize Issues
Rank problems by impact on maintainability, performance, and business value.
Priority levels:
-
High: Security issues, major maintainability problems, performance bottlenecks
-
Medium: Code complexity, duplication, testing gaps
-
Low: Style improvements, minor optimizations
Focus on high-impact, low-risk refactorings first.
- Propose Solutions
For each issue, suggest specific refactoring patterns with concrete examples.
See refactoring-patterns.md for complete pattern catalog.
- Consider Trade-offs
Be pragmatic:
-
Will refactoring introduce complexity?
-
Is there performance overhead?
-
What's the benefit vs. cost?
-
Is this over-engineering?
Remember: Perfect code is less important than working, maintainable code the team can understand.
- Ensure Test Coverage
Before refactoring:
-
✅ Verify existing tests cover the code
-
✅ Suggest additional tests for insufficient coverage
-
✅ Ensure tests pass before and after refactoring
-
✅ Refactor in small, verifiable steps
Quick Refactoring Reference
Extract Method
When: Method > 10-15 lines or does multiple things
Before
def calculate_total subtotal = line_items.sum(&:amount) tax = subtotal * tax_rate shipping = calculate_shipping(subtotal) subtotal + tax + shipping end
After
def calculate_total subtotal + tax + shipping_cost end
private
def subtotal line_items.sum(&:amount) end
def tax subtotal * tax_rate end
def shipping_cost calculate_shipping(subtotal) end
Extract Class
When: Class > 100 lines or has multiple responsibilities
Before: User class handling authentication AND profile management
class User < ApplicationRecord def authenticate(password) # Authentication logic end
def update_profile(params) # Profile logic end
def send_welcome_email # Email logic end end
After: Separated concerns
class User < ApplicationRecord has_one :user_profile
def authenticate(password) # Authentication only end end
class UserProfile < ApplicationRecord belongs_to :user
def update(params) # Profile management end end
class UserNotifier def self.send_welcome(user) # Email logic end end
Extract Service Object
When: Logic spans multiple models or has complex orchestration
Before: Fat controller or model method
class PolicyRenewalService def initialize(policy, new_expiry_date) @policy = policy @new_expiry_date = new_expiry_date end
def call return failure("Not renewable") unless @policy.renewable?
ApplicationRecord.transaction do
archive_old_policy
update_policy
create_invoice
send_notifications
end
success(@policy)
rescue StandardError => e failure(e.message) end end
Replace Conditional with Polymorphism
When: Complex conditionals based on type
Before: Type checking
def calculate_premium case insurance_type when 'auto' base_rate * vehicle_factor * driver_age_factor when 'home' base_rate * property_value_factor * location_risk when 'life' base_rate * age_factor * health_factor end end
After: Polymorphism
class AutoInsurancePolicy < Insurance::Policy def calculate_premium base_rate * vehicle_factor * driver_age_factor end end
class HomeInsurancePolicy < Insurance::Policy def calculate_premium base_rate * property_value_factor * location_risk end end
Introduce Parameter Object
When: Method has > 3 parameters or parameter groups appear together
Before
def create_policy(policy_number, effective_date, expiry_date, premium, person_id, company_id)
...
end
After
class PolicyAttributes attr_reader :policy_number, :effective_date, :expiry_date, :premium, :person_id, :company_id
def initialize(params) @policy_number = params[:policy_number] @effective_date = params[:effective_date] # ... end
def valid? # Validation logic end end
def create_policy(attributes) return unless attributes.valid?
...
end
Ruby and Rails Best Practices
Ruby Idioms
Use blocks and enumerables:
✅ Good
users.select(&:active?).map(&:email)
❌ Bad
result = [] users.each do |user| result << user.email if user.active? end
Use symbols for keys:
✅ Good
{ name: 'John', age: 30 }
❌ Bad
{ 'name' => 'John', 'age' => 30 }
Rails Conventions
Skinny controllers, focused models:
-
Controllers: HTTP handling, authorization
-
Models: Domain logic, associations
-
Services: Multi-model operations
Use scopes for queries:
✅ Good
class Policy < ApplicationRecord scope :active, -> { where(status: 'active') } scope :expiring_soon, -> { where('expiry_date < ?', 30.days.from_now) } end
❌ Bad
def self.active_policies where(status: 'active') end
SOLID Principles
-
Single Responsibility: One class = one reason to change
-
Open/Closed: Open for extension, closed for modification
-
Liskov Substitution: Subclasses should be substitutable
-
Interface Segregation: Many specific interfaces > one general
-
Dependency Inversion: Depend on abstractions, not concretions
Output Format
When providing refactoring recommendations:
- Code Smell Analysis
List identified issues with severity (High/Medium/Low) and location
- Refactoring Plan
Prioritized list of refactoring steps
- Implementation Examples
Concrete before/after code samples
- Test Considerations
Required test changes or additions
- Migration Strategy
How to safely deploy changes
Quality Checks
Before finalizing recommendations:
-
✅ All tests still pass
-
✅ No performance regressions
-
✅ Code complexity improves (ABC score, cyclomatic complexity)
-
✅ Code is more readable and maintainable
-
✅ Business logic unchanged (unless explicitly intended)
Related Documentation
-
code-smells.md - Complete code smell catalog
-
refactoring-patterns.md - Detailed refactoring patterns
-
testing-patterns.md - Testing best practices
Quick Decision Matrix
Smell Pattern When to Use
Long Method Extract Method Method > 10-15 lines
Large Class Extract Class Class > 100 lines
Long Parameter List Parameter Object
3 parameters
Feature Envy Move Method Uses other object's data
Primitive Obsession Extract Value Object Primitives with behavior
Complex Conditional Polymorphism Type-based conditionals
Duplicated Code Extract Method/Module Same code in 2+ places
Communication Style
-
Use clear, technical language for experienced developers
-
Provide concrete examples over abstractions
-
Reference Ruby Science principles by name
-
Include links to documentation when relevant
-
Be decisive but explain reasoning
Ask questions when uncertain about:
-
Business requirements
-
Existing constraints
-
Team preferences
-
Performance requirements
Remember: The goal is maintainable code that the team understands, not perfect code.