Skip to content

Instantly share code, notes, and snippets.

@j-thepac
Created August 13, 2025 00:43
Show Gist options
  • Save j-thepac/0f4fbbfc5a2c40d84ecfa07d69fc7eb0 to your computer and use it in GitHub Desktop.
Save j-thepac/0f4fbbfc5a2c40d84ecfa07d69fc7eb0 to your computer and use it in GitHub Desktop.
Clean Code Summary - Robert C. Martin

Clean Code by Robert C. Martin - Chapter Summaries

Chapter 1: Clean Code

  • Clean code is simple and elegant - reads like well-written prose
  • Clean code is focused - each function, class, and module has a single responsibility
  • Clean code is written by someone who cares - attention to detail is evident
  • Clean code can be read and enhanced by others - not just the original author
  • Clean code has meaningful names - variables, functions, and classes have intention-revealing names
  • Clean code has no duplication - follows the DRY principle
  • Clean code runs all tests - is thoroughly tested and works correctly
  • Clean code expresses the author's intent clearly - no hidden surprises
  • The Boy Scout Rule: Always leave the code cleaner than you found it

Chapter 2: Meaningful Names

  • Use intention-revealing names - variable/function names should explain why they exist and what they do
  • Avoid disinformation - don't use names that vary in small ways or mislead about the variable's purpose
  • Make meaningful distinctions - avoid noise words like "Info", "Data", "Variable" in names
  • Use pronounceable names - makes discussion and code reviews easier
  • Use searchable names - avoid single-letter variables except for short loops
  • Avoid encodings - no Hungarian notation or member prefixes
  • Avoid mental mapping - readers shouldn't have to translate your names into concepts they know
  • Class names should be nouns - Customer, WikiPage, Account
  • Method names should be verbs - postPayment, deletePage, save
  • Pick one word per concept - don't use fetch, retrieve, and get for similar methods
  • Use solution domain names - use computer science terms when appropriate
  • Add meaningful context - use prefixes or enclosing classes to provide context

Chapter 3: Functions

  • Functions should be small - 20 lines or fewer, ideally 4-6 lines
  • Functions should do one thing - they should have a single responsibility
  • One level of abstraction per function - don't mix high and low-level operations
  • Switch statements should appear only once - bury them in low-level classes
  • Use descriptive names - function names should clearly indicate what they do
  • Function arguments should be minimal - zero arguments is ideal, three is maximum
  • Avoid flag arguments - they indicate the function does more than one thing
  • Have no side effects - functions should only do what their name implies
  • Command Query Separation - functions should either do something or answer something, not both
  • Prefer exceptions to returning error codes - makes the code cleaner and more readable
  • Extract try/catch blocks - error handling is one thing, so extract it into separate functions
  • Don't repeat yourself (DRY) - duplication is the root of all evil in software

Chapter 4: Comments

  • Comments are a necessary evil - they compensate for our failure to express intent in code
  • Good code mostly documents itself - clear code with good names needs fewer comments
  • Comments lie - they get outdated as code changes but comments don't
  • Good comments include:
    • Legal comments (copyright, licensing)
    • Informative comments (explaining regex patterns)
    • Explanation of intent
    • Clarification of obscure arguments
    • Warning of consequences
    • TODO comments
    • Amplification of important details
  • Bad comments include:
    • Mumbling or unclear comments
    • Redundant comments that just repeat the code
    • Misleading comments
    • Mandated comments (required by process)
    • Journal comments (change logs)
    • Noise comments
    • Commented-out code
    • HTML comments in source code
    • Too much information

Chapter 5: Formatting

  • Vertical formatting - source files should be small (200-500 lines)
  • Newspaper metaphor - most important concepts first, details follow
  • Vertical openness - blank lines separate concepts
  • Vertical density - related lines should appear vertically dense
  • Vertical distance - related concepts should be vertically close
  • Variable declarations - close to their usage
  • Instance variables - at the top of the class
  • Dependent functions - caller above callee when possible
  • Conceptual affinity - similar functions should be grouped together
  • Horizontal formatting - lines should be short (80-120 characters)
  • Horizontal openness and density - use whitespace to associate related things
  • Horizontal alignment - don't align variable declarations or assignments
  • Indentation - shows the hierarchical structure of the code
  • Team rules - follow consistent formatting rules across the team

Chapter 6: Objects and Data Structures

  • Data/Object Anti-Symmetry - objects hide data and expose functions, data structures expose data
  • The Law of Demeter - objects should only talk to their immediate friends
  • Train wrecks - avoid chaining method calls like a.getB().getC().doSomething()
  • Hybrids - avoid half-object, half-data structure hybrids
  • Hide structure - don't expose internal structure through accessors
  • Data Transfer Objects (DTOs) - classes with public variables and no functions
  • Active Record - special form of DTO with navigation methods
  • Objects should expose behavior and hide data - use methods, not getters/setters
  • Data structures should expose data and have no significant behavior

Chapter 7: Error Handling

  • Use exceptions rather than return codes - exceptions separate error handling from main logic
  • Write your try-catch-finally statement first - helps define transaction boundaries
  • Use unchecked exceptions - checked exceptions violate Open/Closed Principle
  • Provide context with exceptions - include enough information to determine source of error
  • Define exception classes in terms of caller's needs - wrap third-party APIs
  • Define the normal flow - use Special Case Pattern to handle exceptional cases
  • Don't return null - causes NullPointerException problems
  • Don't pass null - even worse than returning null
  • Create informative error messages - include context about what failed and why
  • Separate error handling from business logic - keep them in different functions

Chapter 8: Boundaries

  • Using third-party code - learn and explore boundary APIs through tests
  • Exploring and learning boundaries - write learning tests to understand third-party code
  • Learning tests are better than free - they help detect changes in third-party libraries
  • Using code that does not yet exist - define interfaces for unknown APIs
  • Clean boundaries - avoid letting too much of your code know about third-party APIs
  • Boundary interfaces should be small - don't expose more than necessary
  • Wrap third-party APIs - creates a cleaner interface and makes testing easier
  • Avoid exposing boundaries to client code - keep boundary interfaces internal
  • Write adapter patterns - to convert between your interface and third-party interface

Chapter 9: Unit Tests

  • The Three Laws of TDD:
    1. Don't write production code until you have a failing unit test
    2. Don't write more of a unit test than is sufficient to fail
    3. Don't write more production code than is sufficient to pass the test
  • Keeping tests clean - test code is just as important as production code
  • Tests enable refactoring - without tests, code becomes rigid
  • Clean tests follow 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 boolean output (pass/fail)
    • Timely - tests should be written just before production code
  • One assert per test - ideally, but not always practical
  • Single concept per test - test one thing at a time
  • Build-Operate-Check pattern - arrange, act, assert
  • Domain-specific testing language - create helper functions for common test operations

Chapter 10: Classes

  • Class organization - public constants, private variables, public functions, private utilities
  • Classes should be small - measured by responsibilities, not lines of code
  • Single Responsibility Principle (SRP) - classes should have only one reason to change
  • Cohesion - classes should have small number of instance variables
  • Maintaining cohesion results in many small classes - as methods become cohesive, extract classes
  • Organizing for change - isolate change by following Open/Closed Principle
  • Open/Closed Principle - classes should be open for extension, closed for modification
  • Dependency Inversion Principle - depend on abstractions, not concretions
  • Classes should depend on abstractions - use interfaces and abstract classes
  • Isolate changes through abstractions - use polymorphism to handle variations

Chapter 11: Systems

  • Separate constructing a system from using it - separate startup process from runtime logic
  • Dependency Injection - move responsibility for construction to external mechanism
  • Scale up - systems should start simple and grow complex incrementally
  • Use standards judiciously - don't over-engineer with unnecessary complexity
  • Systems need domain-specific languages - build expressive APIs for your domain
  • Test drive the system architecture - start simple and evolve
  • Optimize decision making - delay decisions until you have the most information
  • Use standards when they add demonstrable value - don't use standards just because they exist
  • Separate concerns at system level - use aspects or similar mechanisms

Chapter 12: Emergence

  • Kent Beck's four rules of Simple Design (in order of importance):
    1. Runs all the tests - system must be verifiably correct
    2. Contains no duplication - eliminate duplicate code
    3. Expresses the intent of programmer - code should be expressive
    4. Minimizes number of classes and methods - keep system small
  • Following these rules leads to emergent design - good design emerges naturally
  • Tests enable emergent design - comprehensive tests allow confident refactoring
  • Refactoring is key - increment functionality while maintaining clean structure
  • Expressive code - choose good names, keep functions small, use standard nomenclature
  • Minimal classes and methods - don't create unnecessary abstractions

Chapter 13: Concurrency

  • Concurrency is hard - it introduces additional complexity and bugs
  • Concurrency myths:
    • Concurrency always improves performance (false)
    • Design doesn't change when writing concurrent programs (false)
    • Understanding concurrency issues isn't important with containers (false)
  • Concurrency defense principles:
    • Single Responsibility Principle - separate concurrency code from other code
    • Limit scope of data - use synchronized keyword to protect critical sections
    • Use copies of data - avoid sharing data when possible
    • Threads should be independent - no shared state
  • Know your library - understand java.util.concurrent package
  • Know your execution models - producer-consumer, readers-writers, dining philosophers
  • Beware dependencies between synchronized methods - can cause deadlocks
  • Keep synchronized sections small - minimize time in critical sections
  • Writing correct shutdown code is hard - consider shutdown early in design
  • Testing threaded code - treat spurious failures as threading issues

Chapter 14: Successive Refinement

  • Case study of Args utility - demonstrates iterative improvement process
  • First draft was messy - but it worked and had tests
  • Refactoring process:
    • Add new functionality
    • Tests start failing
    • Make small changes to pass tests
    • Clean up resulting mess
    • Repeat
  • Incremental improvement - make small changes and keep tests passing
  • Boy Scout Rule applied - continuously clean up the code
  • The result - clean, well-structured code that's easy to understand and modify
  • Key lesson - it's impossible to get it right the first time, but that's okay

Chapter 15: JUnit Internals

  • Case study of JUnit's ComparisonCompactor - real example of code improvement
  • Original code issues:
    • Poor variable names
    • Functions too long
    • Mixed levels of abstraction
  • Refactoring steps:
    • Improve naming
    • Extract methods
    • Eliminate duplication
    • Simplify conditionals
  • Result - more readable and maintainable code
  • Demonstrates - even good code can be made better through careful refactoring

Chapter 16: Refactoring SerialDate

  • Case study of open-source SerialDate class - shows real-world refactoring
  • Problems identified:
    • Inappropriate comments
    • Poor naming
    • Functions doing too many things
    • Unclear abstractions
  • Refactoring approach:
    • Fix obvious problems first
    • Improve naming throughout
    • Extract and simplify methods
    • Eliminate duplication
    • Add tests for safety
  • Outcome - significantly improved code readability and maintainability
  • Lessons - refactoring is an ongoing process that requires patience and discipline

Chapter 17: Smells and Heuristics

❌ CODE SMELLS (What to Avoid)

Comments - Bad Practices:

  • Inappropriate information (change logs, author info)
  • Obsolete comments that no longer apply
  • Redundant comments that just repeat the code
  • Poorly written or unclear comments
  • Commented-out code left in source files

Environment - Problems:

  • Build requires more than one step
  • Tests require more than one step

Functions - Bad Practices:

  • Too many arguments (more than 3)
  • Output arguments (modifying input parameters)
  • Flag arguments (boolean parameters)
  • Dead functions (unused code)

General - Code Smells:

  • Multiple languages in one source file
  • Obvious behavior is unimplemented
  • Incorrect behavior at boundaries
  • Overridden safety mechanisms
  • Code duplication
  • Code at wrong level of abstraction
  • Base classes depending on their derivatives
  • Classes/functions exposing too much information
  • Dead code that's never executed
  • Related code separated vertically
  • Inconsistent naming/formatting
  • Clutter (unused variables, empty methods)
  • Artificial coupling between unrelated modules
  • Feature envy (class using methods of another class excessively)
  • Selector arguments (passing type codes)
  • Obscured intent (unclear purpose)
  • Misplaced responsibility (code in wrong location)
  • Inappropriate static methods
  • Hidden temporal couplings
  • Arbitrary decisions without clear reasoning
  • Transitive navigation (Law of Demeter violations)

Names - Bad Practices:

  • Non-descriptive names
  • Names at wrong abstraction level
  • Ambiguous names
  • Encoded names (Hungarian notation)
  • Names that don't describe side-effects
  • Short names for long scopes

Tests - Problems:

  • Insufficient test coverage
  • Skipping trivial tests
  • Not testing boundary conditions
  • Ignoring tests without investigation
  • Slow tests

✅ GOOD PRACTICES (What to Do)

Functions - Good Practices:

  • Use explanatory variables - break complex expressions into named variables
  • Function names should clearly state what they do
  • Functions should do one thing and do it well
  • Functions should descend only one level of abstraction
  • Understand the algorithm before writing code

General - Good Practices:

  • Make logical dependencies physical - if A depends on B, make it explicit
  • Prefer polymorphism to if/else or switch/case statements
  • Follow standard conventions consistently
  • Replace magic numbers with named constants
  • Be precise in your implementations
  • Use structure over convention when possible
  • Encapsulate conditionals in well-named functions
  • Avoid negative conditionals (use positive logic)
  • Encapsulate boundary conditions in variables
  • Keep configurable data at high levels of the application

Names - Good Practices:

  • Choose descriptive names that reveal intent
  • Choose names at appropriate level of abstraction
  • Use standard nomenclature where possible
  • Use unambiguous names that are clear
  • Use long names for long scopes and short names for short scopes
  • Names should describe side-effects if any exist

Tests - Good Practices:

  • Use coverage tools to identify untested code
  • Test boundary conditions thoroughly
  • Exhaustively test near bugs (bugs cluster)
  • Pay attention to patterns of failure - they reveal design issues
  • Analyze test coverage patterns for insights
  • Keep tests fast so they run frequently

🎯 KEY PRINCIPLES

  • Boy Scout Rule: Leave code cleaner than you found it
  • Single Responsibility: Each module should have one reason to change
  • Open/Closed Principle: Open for extension, closed for modification
  • Don't Repeat Yourself (DRY): Eliminate duplication
  • Law of Demeter: Don't talk to strangers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment