Coding Standards
This skill defines the coding standards that ALL generated code must follow. Consult this before and during code generation.
Core Philosophy
Write code that is easy to change. Code that is easy to change is:
- •Transparent: The consequences of change are obvious
- •Reasonable: The cost of change is proportional to its benefit
- •Usable: Existing code can be reused in new contexts
- •Exemplary: The code encourages those who change it to perpetuate these qualities
Sandi Metz Rules
1. Classes Should Be Small
- •100 lines max per class (excluding whitespace and comments)
- •If a class exceeds this, it's doing too much—extract collaborators
- •A class should have a single, clear responsibility
2. Methods Should Be Small
- •5 lines max per method (excluding def/end)
- •Methods should do one thing
- •If you need a comment to explain what a section does, extract it to a named method
3. Methods Take Few Parameters
- •4 parameters max for any method
- •If you need more, you're missing an object—create one
- •Use keyword arguments for clarity when you have 2+ parameters
- •Consider a configuration object or builder pattern for complex initialization
4. Controllers Instantiate One Object
- •Entry points should create one primary collaborator
- •That object orchestrates the work through its own collaborators
- •Avoid "god controllers" that know about everything
5. Break These Rules Only If You Can Justify Why
SOLID Principles
Single Responsibility Principle (SRP)
- •A class should have only one reason to change
- •Ask: "What does this class do?" If the answer includes "and", split it
- •Test: Can you describe the class without using "and" or "or"?
Open/Closed Principle (OCP)
- •Open for extension, closed for modification
- •Use composition and dependency injection to add behavior
- •New features should add code, not change existing code
Liskov Substitution Principle (LSP)
- •Subtypes must be substitutable for their base types
- •Don't inherit just to reuse code—inherit to be substitutable
- •Prefer composition over inheritance
Interface Segregation Principle (ISP)
- •Depend on narrow interfaces, not fat ones
- •A client shouldn't know about methods it doesn't use
- •Use protocols/abstract base classes to define minimal interfaces
Dependency Inversion Principle (DIP)
- •Depend on abstractions, not concretions
- •High-level modules shouldn't depend on low-level modules
- •Both should depend on abstractions (protocols/interfaces)
Design Patterns to Follow
Dependency Injection
python
# WRONG: Hard dependency
class OrderProcessor:
def __init__(self):
self.payment_gateway = StripeGateway() # Concrete dependency
self.emailer = SMTPEmailer() # Hard to test
# RIGHT: Injected dependencies
class OrderProcessor:
def __init__(self, payment_gateway: PaymentGateway, notifier: Notifier):
self.payment_gateway = payment_gateway
self.notifier = notifier
Composition Over Inheritance
python
# WRONG: Deep inheritance for code reuse
class AdminUser(PowerUser(PremiumUser(User))):
pass
# RIGHT: Compose behaviors
class User:
def __init__(self, permissions: PermissionSet, features: FeatureSet):
self.permissions = permissions
self.features = features
Tell, Don't Ask
python
# WRONG: Asking for data, then acting on it
if order.status == "pending" and order.total > 0:
order.status = "processing"
payment.charge(order.total)
# RIGHT: Tell the object what to do
order.process(payment_gateway)
Object Communication
Public APIs Only
- •Objects communicate through well-defined public methods
- •Never reach into an object's internals
- •Use properties/getters for controlled access to state
- •Private methods (prefixed with
_) are implementation details
Law of Demeter
- •Only talk to your immediate friends
- •Don't chain through objects:
a.b.c.do_thing() - •If you need something from a distant object, ask your collaborator for it
python
# WRONG: Reaching through objects customer.wallet.credit_card.charge(amount) # RIGHT: Tell your immediate collaborator customer.charge(amount)
Encapsulation
Hide Implementation Details
- •Expose what an object does, not how it does it
- •Internal data structures should be private
- •Changes to implementation shouldn't break clients
Immutability Where Possible
- •Prefer immutable objects for value types
- •Use
@dataclass(frozen=True)or explicit immutability - •Mutable state should be carefully controlled
Testing Requirements
Test All Public APIs
- •Every public method must have tests
- •Tests document the expected behavior
- •If it's not tested, it doesn't work
Design for Testability
python
# Classes should be easy to instantiate with test doubles
class OrderService:
def __init__(
self,
repository: OrderRepository,
payment_gateway: PaymentGateway,
event_bus: EventBus,
):
self.repository = repository
self.payment_gateway = payment_gateway
self.event_bus = event_bus
# In tests:
def test_order_processing():
repo = MockOrderRepository()
gateway = MockPaymentGateway()
bus = MockEventBus()
service = OrderService(repo, gateway, bus)
service.process_order(order_id)
assert gateway.charged_amount == 100
assert bus.published_events == [OrderProcessed(order_id)]
Test Isolation
- •Tests should not depend on each other
- •Each test sets up its own state
- •Use dependency injection to provide mocks/stubs/fakes
Test Behavior, Not Implementation
python
# WRONG: Testing implementation
def test_uses_cache():
service.get_user(1)
assert service._cache._data[1] == user # Brittle
# RIGHT: Testing behavior
def test_returns_user():
user = service.get_user(1)
assert user.name == "Alice"
Code Structure
No God Objects
- •No class should know about everything
- •If a class has too many dependencies, it's doing too much
- •Split into focused collaborators with single responsibilities
Clean Object Graph
code
EntryPoint
└── Coordinator
├── ServiceA (with its dependencies)
├── ServiceB (with its dependencies)
└── ServiceC (with its dependencies)
- •Build the object graph at the entry point
- •Objects receive their dependencies, they don't create them
- •Avoid circular dependencies
Principle of Least Surprise
- •Methods should do what their names suggest
- •No hidden side effects
- •Consistent naming conventions
- •Follow language/framework idioms
Checklist Before Committing Code
- • Every class has a single responsibility
- • No class exceeds 100 lines
- • No method exceeds 5 lines
- • No method takes more than 4 parameters
- • All dependencies are injected
- • Objects communicate through public APIs only
- • No Law of Demeter violations (no long chains)
- • All public methods have tests
- • Tests use mocks/stubs for dependencies
- • Code does what its names suggest