|
| 1 | +# Code quality principles |
| 2 | + |
| 3 | +Universal principles distilled from Kent Beck's Smalltalk Best Practice Patterns, generalized for any codebase. For **stack-specific commands, agent roles, and rules that conflict with these principles**, the nearest [`AGENTS.md`](../../../AGENTS.md) wins. |
| 4 | + |
| 5 | +## 1. Composed Method |
| 6 | + |
| 7 | +Divide every function into sub-functions that each perform one identifiable task. Keep all operations in a method at the same level of abstraction. This naturally produces many small methods, each a few lines long. |
| 8 | + |
| 9 | +## 2. Intention-Revealing Names |
| 10 | + |
| 11 | +Name methods and functions after what they accomplish, never how they accomplish it. A reader should understand the purpose of a call without reading its body. |
| 12 | + |
| 13 | +## 3. Replace Comments with Clear Code |
| 14 | + |
| 15 | +If a comment restates what the code does, delete it. If you cannot delete a comment, refactor the code (extract a well-named function, rename a variable) until the comment is redundant. Reserve comments for why, not what. |
| 16 | + |
| 17 | +## 4. Constructor Clarity |
| 18 | + |
| 19 | +Provide factory functions or constructors that create well-formed instances. Pass all required parameters upfront so callers never receive half-initialized objects. |
| 20 | + |
| 21 | +## 5. Single Responsibility for Methods |
| 22 | + |
| 23 | +Each method should have exactly one reason to change. If a method requires a paragraph to explain, it is doing too much. |
| 24 | + |
| 25 | +## 6. Say Things Once and Only Once |
| 26 | + |
| 27 | +Every piece of knowledge or logic should exist in exactly one place. Duplicate code is a multiple-update liability—extract it. |
| 28 | + |
| 29 | +## 7. Behavior Over State |
| 30 | + |
| 31 | +Get the behavior (public interface) right first. Internal representation can always change later if it is hidden behind a clean API. Optimizing data layout prematurely couples consumers to implementation details. |
| 32 | + |
| 33 | +## 8. Intention-Revealing Selectors / Function Names |
| 34 | + |
| 35 | +Name functions after the concept they represent, not the algorithm they use. `includes(item)` is better than `linearSearchFor(item)`. Imagine a second, very different implementation—would you give it the same name? If not, generalize. |
| 36 | + |
| 37 | +## 9. Guard Clauses Over Deep Nesting |
| 38 | + |
| 39 | +Handle edge cases and error conditions at the top of a function and return early. The main logic path should read without indentation. |
| 40 | + |
| 41 | +## 10. Query Methods Return; Commands Mutate |
| 42 | + |
| 43 | +Separate functions that answer questions (return a value, no side effects) from functions that change state. Name query methods with `is`, `has`, `can` prefixes for booleans. |
| 44 | + |
| 45 | +## 11. Explaining Variables |
| 46 | + |
| 47 | +When a complex expression is hard to read, assign its result to a well-named local variable. The variable name becomes the explanation. |
| 48 | + |
| 49 | +## 12. Role-Suggesting Names |
| 50 | + |
| 51 | +Name variables after the role they play, not their type. `employees` not `employeeList`; `query` not `queryString`. The type can be inferred from context. |
| 52 | + |
| 53 | +## 13. Use Polymorphism Instead of Conditionals |
| 54 | + |
| 55 | +When the same if/switch structure appears in multiple places, replace it with polymorphic objects that each implement one branch. Adding a new case becomes adding a new class, not editing existing code. |
| 56 | + |
| 57 | +## 14. Delegate, Do Not Inherit (Prefer Composition) |
| 58 | + |
| 59 | +Share implementation by passing work to a collaborator object rather than subclassing. Delegation keeps the two objects independently replaceable and avoids deep inheritance hierarchies. |
| 60 | + |
| 61 | +## 15. Method Object for Complex Logic |
| 62 | + |
| 63 | +When a method has grown huge and shares many temporaries, extract the entire computation into its own class. Turn parameters and temporaries into instance fields, put the logic in a `compute()`/`call()` method, then simplify with Composed Method. |
| 64 | + |
| 65 | +## 16. Execute Around (Resource Bracketing) |
| 66 | + |
| 67 | +When two actions must always happen together (open/close, lock/unlock, setup/teardown), expose a single function that accepts a callback. The caller never forgets the second action. |
| 68 | + |
| 69 | +## 17. Explicit Initialization |
| 70 | + |
| 71 | +Initialize all state at construction time. Never rely on callers to set fields in the right order after creation. If defaults exist, set them in the constructor. |
| 72 | + |
| 73 | +## 18. Lazy Initialization |
| 74 | + |
| 75 | +When computing or fetching a value is expensive and may not be needed, defer it to first access. Cache the result in a field and return it on subsequent calls. |
| 76 | + |
| 77 | +## 19. Constant Methods / Named Constants |
| 78 | + |
| 79 | +Replace magic literals with named constants or zero-argument methods. `MAX_RETRIES` communicates more than `5`, and a method lets subclasses override the value. |
| 80 | + |
| 81 | +## 20. Indirect Variable Access (Encapsulate Fields) |
| 82 | + |
| 83 | +Access instance fields through getter/setter methods rather than directly. This gives you a single place to add validation, logging, lazy init, or change notification later. |
| 84 | + |
| 85 | +## 21. Collection Accessor Safety |
| 86 | + |
| 87 | +Never return a raw mutable collection from a getter. Return a copy, an immutable view, or expose only domain-specific add/remove/enumerate methods. |
| 88 | + |
| 89 | +## 22. Equality and Hashing Contract |
| 90 | + |
| 91 | +If you override equality, override hashing to match. Objects that are equal must produce the same hash. Base both on the same set of fields. |
| 92 | + |
| 93 | +## 23. Mediating Protocol |
| 94 | + |
| 95 | +When two objects collaborate heavily, make the set of messages between them explicit and consistent. Name them coherently so a third party can implement the same interface. |
| 96 | + |
| 97 | +## 24. Double Dispatch for Cross-Type Operations |
| 98 | + |
| 99 | +When behaviour depends on the types of two objects (not just the receiver), use double dispatch: the receiver calls back the argument with a more specific method, including its own type in the name. |
| 100 | + |
| 101 | +## 25. Pluggable Behaviour Over Subclass Explosion |
| 102 | + |
| 103 | +When many subclasses differ in only one or two methods, replace the hierarchy with a single class that accepts a strategy (callback, lambda, or strategy object). Reserve subclassing for genuinely different families of behaviour. |
| 104 | + |
| 105 | +## 26. Collecting Parameter |
| 106 | + |
| 107 | +When multiple sub-methods need to contribute to a single result collection, pass the collection as a parameter rather than concatenating return values or stashing state in a field. |
| 108 | + |
| 109 | +## 27. Interesting Return Values Only |
| 110 | + |
| 111 | +A method should return a value only when the caller needs it. Do not return `self` or internal state by default—return something meaningful or nothing at all. Make return values intentional. |
| 112 | + |
| 113 | +## 28. Reversing Method for Readable Flow |
| 114 | + |
| 115 | +If sending messages to multiple receivers in sequence breaks readability, add a convenience method on the parameter so all calls flow through one object. Readable left-to-right flow matters. |
| 116 | + |
| 117 | +## 29. Debug Printing for Developer Ergonomics |
| 118 | + |
| 119 | +Override `toString`/`__repr__`/`inspect` to show the structural information a developer needs when debugging. User-facing display strings are a separate concern. |
| 120 | + |
| 121 | +## 30. Adopt Patterns Incrementally |
| 122 | + |
| 123 | +Do not try to apply all rules at once. Write code, notice friction, then apply the pattern that resolves it. Patterns are refactoring targets, not upfront mandates. Clean up as you go. |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## How to use this file |
| 128 | + |
| 129 | +Use it for code review, pairing, and AI-assisted development. Reference a principle by number (for example, "Principle 8: name after the concept, not the algorithm"). This file is the canonical list; [`AGENTS.md`](../../../AGENTS.md) defines project-specific rules and agent roles. |
0 commit comments