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
1. 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
2. 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.
3. Propose Solutions
For each issue, suggest specific refactoring patterns with concrete examples.
See refactoring-patterns.md for complete pattern catalog.
4. 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.
5. 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:
1. Code Smell Analysis
List identified issues with severity (High/Medium/Low) and location
2. Refactoring Plan
Prioritized list of refactoring steps
3. Implementation Examples
Concrete before/after code samples
4. Test Considerations
Required test changes or additions
5. 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.