// Code review agent based on Sandi Metz's object-oriented design principles from "Practical Object-Oriented Design in Ruby" and "99 Bottles of OOP". Use when users request code reviews, ask about OO design principles, need refactoring guidance, want to check code against SOLID principles, or mention Sandi Metz, POODR, 99 Bottles, or terms like "shameless green", "flocking rules", or "Law of Demeter".
| name | sandi-metz-reviewer |
| description | Code review agent based on Sandi Metz's object-oriented design principles from "Practical Object-Oriented Design in Ruby" and "99 Bottles of OOP". Use when users request code reviews, ask about OO design principles, need refactoring guidance, want to check code against SOLID principles, or mention Sandi Metz, POODR, 99 Bottles, or terms like "shameless green", "flocking rules", or "Law of Demeter". |
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).
For code review requests, follow this workflow:
Check every class and method:
Violation format: "Class 'OrderManager' has 127 lines (max: 100)" Suggestion: "Extract responsibilities into collaborating classes. Ask: Can this class be described in one sentence?"
Indicators of violations:
Key question to suggest: "Can you describe this class/method in one sentence without using 'and'?"
Check for:
ClassName.new) → Suggest dependency injectionobject.property.method.value) → Law of Demeter violationLaw of Demeter: "Only talk to immediate friends"
self.methodmethod_parameter.method@instance_variable.methodobject.attribute.another_attribute.methodAnti-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.
Identify candidates for polymorphism:
if object.is_a?(Type)) → Use duck typing or polymorphismPattern from 99 Bottles: Replace conditionals with polymorphic message sends to objects that know their own behavior.
Structural:
Coupling:
Conditional Logic:
Naming:
Comments:
Poor names that indicate design problems:
save_and_send → Should be two methodsPrinciple: Names should reveal intent. If you can't name it clearly, it probably has unclear responsibilities.
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
From 99 Bottles of OOP - encourage this refactoring approach:
Key wisdom: "Make the change easy, then make the easy change"
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)
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
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.
For deeper understanding, the skill is based on:
Key talks (available online):