Created
November 13, 2025 23:25
-
-
Save timcunningham/e60e8bdc92e1eb6fc4f8aee5678bdb1f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ## Clean Code Principles (Bob Martin) | |
| These principles apply to ALL code you write, regardless of language: | |
| ### Meaningful Names | |
| - **Use intention-revealing names**: Names should answer why it exists, what it does, and how it's used | |
| - **Avoid disinformation**: Don't use names that obscure meaning (e.g., `accountList` when it's not actually a list) | |
| - **Make meaningful distinctions**: Don't use number-series naming (`a1`, `a2`) or noise words (`ProductInfo` vs `ProductData`) | |
| - **Use pronounceable names**: If you can't pronounce it, you can't discuss it intelligently | |
| - **Use searchable names**: Single-letter names and numeric constants are hard to locate across a codebase | |
| - **Avoid encodings**: Don't prefix with type or scope (no Hungarian notation, no `m_` prefixes) | |
| - **Class names**: Should be nouns or noun phrases (`Customer`, `WikiPage`, `Account`) | |
| - **Method names**: Should be verbs or verb phrases (`postPayment`, `deletePage`, `save`) | |
| - **Pick one word per concept**: Don't mix `fetch`, `retrieve`, and `get` for the same concept | |
| - **Use solution domain names**: Use CS terms, algorithm names, pattern names when appropriate | |
| - **Add meaningful context**: Use prefixes/namespaces when needed (`addrFirstName` vs `firstName` when context unclear) | |
| ### Functions | |
| - **Small**: Functions should be small. Then make them smaller. 20 lines is generous, 5-10 lines is ideal | |
| - **Do one thing**: Functions should do ONE thing, do it well, do it only | |
| - **One level of abstraction per function**: Don't mix high-level concepts with low-level details | |
| - **Reading code from top to bottom**: Code should read like a narrative, with descending levels of abstraction | |
| - **Switch statements**: Bury in low-level classes, never repeat. Use polymorphism instead | |
| - **Use descriptive names**: Long descriptive names are better than short enigmatic names | |
| - **Function arguments**: | |
| - Zero arguments (niladic) is ideal | |
| - One argument (monadic) is good | |
| - Two arguments (dyadic) is acceptable but scrutinize | |
| - Three arguments (triadic) should be avoided when possible | |
| - More than three (polyadic) requires special justification | |
| - **Flag arguments are ugly**: Passing a boolean is a terrible practice (function does more than one thing) | |
| - **Argument objects**: When functions need >2-3 arguments, wrap them in a class/object | |
| - **Have no side effects**: Don't have hidden side effects. Don't modify arguments. Don't set unexpected state | |
| - **Command Query Separation**: Functions should either DO something or ANSWER something, never both | |
| - **Prefer exceptions to returning error codes**: Use exceptions for error handling, not return codes | |
| - **Extract try/catch blocks**: Error handling is one thing. Extract the bodies of try/catch into their own functions | |
| - **Don't repeat yourself (DRY)**: Duplication is the root of all evil in software | |
| ### Comments | |
| - **Don't comment bad code—rewrite it**: Comments are a failure to express yourself in code | |
| - **Explain yourself in code**: `if (employee.isEligibleForFullBenefits())` is better than `// Check if eligible` | |
| - **Good comments**: | |
| - Legal comments (copyright, licenses) | |
| - Informative comments (explaining regex patterns, return values) | |
| - Explanation of intent | |
| - Warning of consequences | |
| - TODO comments (but track them) | |
| - Amplification (emphasizing importance of something that might seem inconsequential) | |
| - **Bad comments**: | |
| - Mumbling: If you must comment, make sure it's clear | |
| - Redundant comments: Don't state the obvious | |
| - Misleading comments: Worse than no comment | |
| - Mandated comments: Not every function needs a comment | |
| - Journal comments: Use source control instead | |
| - Noise comments: Restating the obvious with no new information | |
| - Position markers: `// Actions //////////////` | |
| - Closing brace comments: `} // end if` | |
| - Attributions and bylines: Source control remembers who wrote what | |
| - Commented-out code: Delete it. Source control remembers | |
| - HTML comments: Comments should be readable in plain text | |
| - Nonlocal information: Don't put system-wide info in local comment | |
| - Too much information: Don't put historical essays or details in comments | |
| - Inobvious connection: Connection between comment and code should be obvious | |
| ### Formatting | |
| - **The purpose of formatting is communication**: Code formatting is about communication, and communication is the professional developer's first order of business | |
| - **Vertical formatting**: | |
| - Small files are easier to understand than large files (200-500 lines max per file) | |
| - Newspaper metaphor: Name tells you if you're in the right module. Top gives high-level concepts. Detail increases as you move down | |
| - Vertical openness: Blank lines separate concepts | |
| - Vertical density: Lines of code that are tightly related should appear vertically dense | |
| - Vertical distance: Concepts that are closely related should be vertically close | |
| - Variable declarations: As close to their usage as possible | |
| - Instance variables: At the top of the class | |
| - Dependent functions: If one function calls another, they should be vertically close, caller above callee | |
| - Conceptual affinity: Stronger the affinity, less vertical distance | |
| - **Horizontal formatting**: | |
| - Keep lines short (80-120 characters as a guideline) | |
| - Use horizontal white space to associate strongly related things and disassociate weakly related | |
| - Don't align declarations in columns (it emphasizes wrong things) | |
| - Indentation is critical: Never break indentation rules even for short if/while statements | |
| - **Team rules**: Every team should agree on a single formatting style and ALL members should use it | |
| ### Objects and Data Structures | |
| - **Data abstraction**: Hide implementation. Don't just add getters/setters | |
| - **Objects expose behavior and hide data**: This makes it easy to add new objects without changing behaviors | |
| - **Data structures expose data and have no behavior**: This makes it easy to add new behaviors to existing data structures | |
| - **The Law of Demeter**: A method should not invoke methods on objects returned by any of its methods | |
| - Talk to friends, not strangers | |
| - Avoid chains: `a.getB().getC().doSomething()` is bad (train wreck) | |
| - Objects should expose behavior, not their internals | |
| - **Data Transfer Objects (DTOs)**: Simple data structures with public variables and no functions are useful for communicating with databases, sockets, etc. | |
| ### Error Handling | |
| - **Use exceptions rather than return codes**: Exceptions separate error handling from happy path | |
| - **Write your try-catch-finally statement first**: This helps define what the user should expect, no matter what goes wrong | |
| - **Use unchecked exceptions**: Checked exceptions violate Open/Closed Principle (in languages that have them) | |
| - **Provide context with exceptions**: Create informative error messages with operation intent and type of failure | |
| - **Define exception classes in terms of caller's needs**: Wrap third-party APIs so you can switch libraries easily and not be tied to API design choices | |
| - **Don't return null**: Returning null creates work for callers (null checks everywhere) and one missed check causes problems | |
| - **Don't pass null**: Passing null into methods is worse than returning null. Avoid it when possible | |
| ### Boundaries | |
| - **Using third-party code**: Learning tests - write tests to explore the third-party API | |
| - **Clean boundaries**: Wrapping third-party code provides a clear boundary and makes it easier to migrate to different library | |
| - **Code at boundaries needs clear separation**: Avoid letting too much of your code know about the third-party particulars | |
| ### Unit Tests | |
| - **The Three Laws of TDD**: | |
| 1. You may not write production code until you have written a failing unit test | |
| 2. You may not write more of a unit test than is sufficient to fail | |
| 3. You may not write more production code than is sufficient to pass the currently failing test | |
| - **Keeping tests clean**: Test code is just as important as production code | |
| - **Tests enable change**: Without tests, you can't refactor. You can't improve the design | |
| - **Clean tests**: Readability, readability, readability - clarity, simplicity, density of expression | |
| - **One assert per test**: Minimize the number of asserts per test (single concept per test) | |
| - **F.I.R.S.T**: | |
| - **Fast**: Tests should run quickly | |
| - **Independent**: Tests should not depend on each other | |
| - **Repeatable**: Tests should be repeatable in any environment | |
| - **Self-Validating**: Tests should have a boolean output (pass/fail) | |
| - **Timely**: Write tests in a timely fashion (just before production code) | |
| ### Classes | |
| - **Class organization**: Public static constants, private static variables, private instance variables, then public functions, then private utilities called by public functions | |
| - **Classes should be small**: Measured by responsibilities, not lines of code | |
| - **Single Responsibility Principle (SRP)**: A class should have one, and only one, reason to change | |
| - **Cohesion**: Classes should have a small number of instance variables. Methods should manipulate one or more of those variables. High cohesion means methods and variables are co-dependent | |
| - **Maintaining cohesion results in many small classes**: When classes lose cohesion, split them | |
| - **Organizing for change**: Classes should be open for extension but closed for modification (Open-Closed Principle) | |
| - **Isolating from change**: Depend on abstractions, not concretions (Dependency Inversion Principle) | |
| ### Systems | |
| - **Separate constructing a system from using it**: Separate startup process from runtime logic | |
| - **Dependency Injection**: Separate construction from use via DI/IoC containers | |
| - **Scale up**: Software systems are unique compared to physical systems - they can grow incrementally IF we maintain proper separation of concerns | |
| - **Use standards wisely, when they add demonstrable value**: Don't use standards just because they exist | |
| - **Domain-Specific Languages**: Allow domain experts to write code in their domain language | |
| ### Emergence (Simple Design) | |
| Kent Beck's four rules of Simple Design (in priority order): | |
| 1. **Runs all the tests**: A design must produce a system that acts as intended | |
| 2. **Contains no duplication**: Duplication is the enemy of a well-designed system | |
| 3. **Expresses the intent of the programmer**: Choose good names. Keep functions and classes small. Use standard nomenclature | |
| 4. **Minimizes the number of classes and methods**: Keep function, class, and module counts low. Don't be dogmatic about small classes/functions if it doesn't add value | |
| ### Concurrency (Where Applicable) | |
| - **Keep concurrency code separate**: Don't mix concurrency code with other code | |
| - **Limit the scope of data**: Use synchronized/locked blocks to protect critical sections | |
| - **Use copies of data**: Avoid sharing data when possible. Use immutable objects | |
| - **Threads should be as independent as possible**: Attempt to partition data into independent subsets | |
| ### Successive Refinement | |
| - **Make it work, then make it right**: First get it working, then refactor to clean it up | |
| - **Incrementalism**: Software development is iterative and incremental | |
| - **Clean code is not written by following rules**: It's written by laboriously applying principles through successive refinement |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment