Critical rules
- •ALWAYS use British English in code, comments and documentation
- •ALWAYS follow Clean Code, SOLID and Object Calisthenics principles
- •NEVER modify build files (e.g.,
pom.xml,build.gradle,package.json,Cargo.toml) unless explicitly requested - •ALWAYS write self-documenting code; add comments only when necessary
- •ALWAYS validate domain constraints in Value Objects
- •NEVER expose mutable state through public methods
- •ALWAYS prefer composition over inheritance
Note: These principles are language-agnostic and apply to all programming languages.
Workflow
- •Review naming — Ensure all identifiers reveal intent without abbreviations
- •Check method/function size — Methods should be < 20 lines, ideally < 10
- •Verify single responsibility — Each class/module/function does one thing
- •Assess nesting — Maximum one level of indentation per method/function (extract logic if needed)
- •Eliminate conditional branches — Use early returns, guard clauses, and polymorphism instead of
else - •Wrap primitives — Domain concepts must use Value Objects
- •Review dependencies — Ensure dependency inversion (depend on abstractions)
- •Check immutability — Prefer immutable objects and limit mutability
- •Validate tests — Ensure behaviour is covered with clear test names
Code Quality Standards
Clean Code Principles
- •Single purpose: Methods should do one thing and do it well
- •Short methods: Keep methods under 20 lines (ideally < 10)
- •Descriptive names: Use names that reveal intent without comments
- •No side effects: Functions should not modify state unexpectedly
- •Immutability: Prefer immutable objects; use immutability features of your language
- •Low coupling: Minimise dependencies between modules
- •High cohesion: Keep related functionality together
Code Readability
- •Use whitespace strategically: Separate logical blocks with blank lines to improve readability
- •Avoid dense code blocks: Large sections of code without separation are hard to follow
- •One blank line between logical sections: Between variable declaration and first operation, between setup and action, between action and assertion
- •Group related statements: Keep related operations together, separate different concerns
Examples
✅ Good: Clear separation of logical blocks
code
var user = new User("john@example.com");
var email = user.getEmail();
var result = validateEmail(email); // Clear separation from setup
assert(result); // Separated from action
assertEquals("john@example.com", user.getEmail());
❌ Bad: Dense code without breathing room
code
var user = new User("john@example.com");
var email = user.getEmail();
var result = validateEmail(email);
assert(result);
assertEquals("john@example.com", user.getEmail());
SOLID Principles
- •Single Responsibility: One class, one reason to change
- •Open/Closed: Open for extension, closed for modification
- •Liskov Substitution: Subtypes must be substitutable for base types
- •Interface Segregation: Small, focused interfaces (prefer roles over wide contracts)
- •Dependency Inversion: Depend on abstractions, not concretions
Object Calisthenics
- •One level of indentation per method — Extract nested logic into separate functions/methods
- •Avoid conditional chains — Prefer guard clauses and polymorphism over
else - •Wrap all primitives — Domain concepts deserve Value Objects (e.g.,
EmailAddress,Money) - •First-class collections — If a class has a collection, it should have no other instance variables
- •One dot per line — Avoid method/property chains (Law of Demeter)
- •Don't abbreviate names —
usernotusr,calculatenotcalc - •Keep entities small — Typical classes <= 50 lines, typical methods <= 10 lines
- •Limit instance variables — Prefer fewer instance variables to promote cohesion
- •Encapsulation — Tell objects what to do instead of asking for state and manipulating it
Decision Trees
When to Create a Value Object
code
Does the primitive have domain meaning? ├─ YES → Does it have validation rules? │ ├─ YES → Create Value Object with validation │ └─ NO → Consider if it will in future; if likely, create Value Object └─ NO → Keep as primitive (e.g., loop counters, temporary variables)
When to Extract a Method/Function
code
Is the method/function > 10 lines?
├─ YES → Extract cohesive blocks into separate methods
└─ NO → Does it have nested conditionals?
├─ YES → Extract conditional logic into named methods
└─ NO → Does it mix abstraction levels?
├─ YES → Separate high-level orchestration from low-level details
└─ NO → Method is acceptable
When to Use Inheritance vs Composition
code
Is there a true "is-a" relationship? ├─ YES → Can you guarantee Liskov Substitution? │ ├─ YES → Inheritance may be appropriate │ └─ NO → Use composition with interfaces └─ NO → ALWAYS use composition
Examples
Good: Value Object with Validation
A Value Object encapsulates a domain concept:
- •Validates the value during creation
- •Immutable after creation
- •Examples:
EmailAddress,Money,UserId,OrderNumber - •The object ensures its own invariants are maintained
Good: Small, Single-Purpose Method
A method or function that:
- •Has one clear purpose
- •Is under 10 lines long
- •Has no side effects
- •Has clear input/output contracts
Good: Tell, Don't Ask
Instead of extracting state and manipulating it:
code
✓ Tell the object what to do: user.activate() ✗ Don't extract state and act on it externally ✗ Don't expose getters/setters for internal mutation
Good: Early Return / Guard Clauses
Structure decision logic with guards at the start:
code
Guard clause: if (condition) return early; Guard clause: if (condition) return early; Guard clause: if (condition) return early; // Happy path at normal indentation level
Good: Dependency Inversion (Hexagonal Architecture)
code
✓ Define an interface/protocol for external dependencies ✓ Inject the abstraction into business logic ✓ Implement the abstraction in adapters for specific technologies ✓ Business logic does not depend on technology details
Bad: Primitive Obsession
code
✗ Passing domain concepts as primitives (String, Integer, etc.) ✗ Scattering validation logic across multiple functions ✓ Wrap domain concepts in Value Objects ✓ Validation happens once during object creation
Bad: Multiple Responsibilities
code
✗ One class/module handling: user creation, email, logging, reporting ✓ Split into separate classes/modules: - UserRegistrationService (handle user creation) - EmailService (send emails) - AuditLogger (log activity) - ReportGenerator (generate reports)
Bad: Deep Nesting
Deep conditional nesting obscures intent:
code
✗ Multiple nested levels: if (x) { if (y) { if (z) { ... } } }
✓ Use guard clauses to exit early
✓ Keep happy path at original indentation
Bad: Excessive Chaining (Train Wreck)
code
✗ Violates Law of Demeter: object.property().method().thing() ✗ Tight coupling to internal structure ✓ Ask the object directly for what you need ✓ Encapsulate navigation in methods
Common Pitfalls
- •Ignoring immutability — Objects should not change after creation unless essential
- •Over-using inheritance — Inheritance creates tight coupling; prefer composition
- •Exposing internal state — Using getters/setters breaks encapsulation
- •Writing God classes/modules — Classes that know or do too much
- •Deep nesting — More than one level of indentation obscures intent
- •Abbreviations —
usr,mgr,calcare unclear; spell out full words - •Framework coupling — Business logic depending on infrastructure/framework details
- •Mutable Value Objects — Value Objects must be immutable
- •Ignoring null/option safety — Use explicit Optional/Maybe types to represent nullable values
- •Comments that explain bad code — Refactor the code instead of explaining it
Checklist
Before submitting code, verify:
- • All primitives with domain meaning are wrapped in Value Objects
- • No method/function exceeds reasonable length (ideally < 10 lines)
- • No class/module exceeds reasonable size (ideally <= 50 lines)
- • Maximum one level of indentation per method/function
- • No
elsekeywords or equivalents (use early returns or polymorphism) - • All objects/variables are immutable unless mutability is essential
- • No getters/setters exposing internal mutable state
- • Domain logic is independent from infrastructure/framework details
- • Tests cover all business rules with expressive names
- • British English spelling throughout
Related Skills
- •
conventional-commits— For commit message standards