Sandi Metz Code Reviewer
Review code using Sandi Metz's principles: Single Responsibility, SOLID, Law of Demeter, "Tell Don't Ask", and the four famous rules (classes ≤100 lines, methods ≤5 lines, parameters ≤4, instance variables ≤4).
Quick Start
For code review requests, follow this workflow:
-
Parse the code - Understand structure: classes, methods, dependencies
-
Apply checks - Run through all principle categories
-
Provide feedback - Clear issues with actionable suggestions
-
Format output - Organized by principle with severity levels
Review Checks
- Sandi Metz's Four Rules
Check every class and method:
-
Classes: Max 100 lines
-
Methods: Max 5 lines (excluding blank lines and end statements)
-
Parameters: Max 4 per method
-
Instance Variables: Max 4 per class
Violation format: "Class 'OrderManager' has 127 lines (max: 100)" Suggestion: "Extract responsibilities into collaborating classes. Ask: Can this class be described in one sentence?"
- Single Responsibility Principle (SRP)
Indicators of violations:
-
Class has >7 public methods → Too many responsibilities
-
Method name contains "and" → Doing multiple things
-
Method doesn't use any instance variables → Feature Envy, belongs elsewhere
-
Hard to describe class in one sentence → Multiple responsibilities
Key question to suggest: "Can you describe this class/method in one sentence without using 'and'?"
- Dependency Management
Check for:
-
Explicit instantiation (ClassName.new ) → Suggest dependency injection
-
Message chains (object.property.method.value ) → Law of Demeter violation
-
Inappropriate intimacy (accessing internals of other objects) → Use proper interfaces
Law of Demeter: "Only talk to immediate friends"
-
✅ self.method
-
✅ method_parameter.method
-
✅ @instance_variable.method
-
❌ object.attribute.another_attribute.method
- Tell, Don't Ask
Anti-pattern:
if user.admin? user.delete_all end
Better:
user.perform_admin_action(:delete_all)
Principle: Objects should make their own decisions, not have their state queried and then acted upon.
- Open/Closed Principle
Identify candidates for polymorphism:
-
Case statements on type → Create subclasses with polymorphic behavior
-
If-elsif chains based on type → Replace with strategy pattern
-
Type checking (if object.is_a?(Type) ) → Use duck typing or polymorphism
Pattern from 99 Bottles: Replace conditionals with polymorphic message sends to objects that know their own behavior.
- Code Smells (18 types)
Structural:
-
Long Method (>5 lines)
-
Large Class (>100 lines)
-
Long Parameter List (>4 params)
-
Data Clump (same params together repeatedly)
Coupling:
-
Feature Envy (method uses data from another class more than own)
-
Message Chains (Law of Demeter violations)
-
Inappropriate Intimacy (classes too tightly coupled)
Conditional Logic:
-
Conditional Complexity (nested if-elsif)
-
Case Statements (candidate for polymorphism)
-
Speculative Generality (code added "just in case")
Naming:
-
Vague names (Manager, Handler, Processor, Data)
-
Methods with "and" (doing multiple things)
-
Flag parameters (boolean params that change behavior)
Comments:
-
Comments explaining "what" code does → Code should be self-documenting
-
Keep comments that explain "why" decisions were made
- Naming Conventions
Poor names that indicate design problems:
-
Classes: Manager, Handler, Processor, Controller, Helper, Util
-
Methods: process, handle, manage, do, data, info
-
With "and": save_and_send → Should be two methods
Principle: Names should reveal intent. If you can't name it clearly, it probably has unclear responsibilities.
Output Format
Structure feedback by principle area:
📏 SANDI METZ'S FOUR RULES ✓ Pass: Class 'Order' size good (45 lines) ⚠️ Warning: Method 'process' has 12 lines (max: 5) [line 23] 💡 Extract smaller methods with intention-revealing names
🎯 SINGLE RESPONSIBILITY ℹ️ Info: Class 'OrderManager' has 9 public methods [line 1] 💡 Ask: Can this class be described in one sentence?
🔗 DEPENDENCIES ❌ Error: Message chain detected: customer.address.street.name [line 45] 💡 Use delegation. Add customer.street_name method
💬 TELL, DON'T ASK
⚠️ Warning: Conditional based on object query [line 67]
💡 Let objects make their own decisions
📊 SUMMARY ✓ Passes: 12 ℹ️ Info: 5 ⚠️ Warnings: 8 ❌ Errors: 2
Severity Levels
-
❌ Error: Serious violations (accessing internals, tight coupling)
-
⚠️ Warning: Rule violations, should be fixed
-
ℹ️ Info: Suggestions, best practices
-
✓ Pass: Correctly following principles
Shameless Green Philosophy
From 99 Bottles of OOP - encourage this refactoring approach:
-
Start with Shameless Green - Write simplest code that works
-
Wait for duplication - Don't abstract too early
-
Follow Flocking Rules:
-
Find things that are most alike
-
Find smallest difference between them
-
Make simplest change to remove difference
-
Converge on abstractions - Let patterns emerge
Key wisdom: "Make the change easy, then make the easy change"
Refactoring Patterns
Suggest these when appropriate:
Extract Method: When methods too long
Before: 15-line method
After: 3-line method calling 3 extracted methods (each ≤5 lines)
Extract Class: When classes have too many responsibilities
Before: OrderManager with 8 instance variables
After: Order + Payment + Shipping (each with ≤4 instance variables)
Replace Conditional with Polymorphism: For case/if-elsif on type
Before: case type when 'book'... when 'electronics'...
After: Book.price, Electronics.price (each knows own behavior)
Introduce Parameter Object: For long parameter lists
Before: create_order(name, email, street, city, state, zip)
After: create_order(customer_info)
Code Examples
When showing before/after examples, keep them concise:
Before (violations):
class OrderManager def process(name, email, address, phone, items, discount, method) # 20 lines of nested conditionals end end
After (principles applied):
class Order def total items.sum(&:price) - discount.amount end end
class Discount def amount # polymorphic behavior end end
When Breaking Rules is OK
Sandi Metz: "Break them only if you have a good reason and you've tried not to."
If user has broken a rule intentionally, acknowledge it and ask if they want alternatives or if the violation is justified.
References
For deeper understanding, the skill is based on:
-
Practical Object-Oriented Design in Ruby (POODR) by Sandi Metz
-
99 Bottles of OOP by Sandi Metz, Katrina Owen, TJ Stankus
Key talks (available online):
-
"Nothing is Something" - RailsConf 2015
-
"All the Little Things" - RailsConf 2014
Execution Notes
-
Focus on one principle area at a time
-
Provide specific line numbers when possible
-
Always include actionable suggestions, not just criticism
-
Celebrate what's done well (✓ Pass messages)
-
Keep feedback encouraging - design is about making code easier to change, not achieving perfection