Clean Code

A Handbook of Agile Software Craftsmanship

technology
software development
programming
agile

1 Listen to Clean Code Summary

2 Book Summary: Clean Code by Robert C. Martin

“Clean Code” by Robert C. Martin, affectionately known as “Uncle Bob,” argues that writing clean code is a professional discipline and a crucial survival skill. Bad code, or a “mess”, actively hinders development, decreases productivity, and can ultimately lead to project failure. The book is divided into principles of clean code, case studies of refactoring, and a list of “smells” or heuristics to identify code that needs cleaning.

“Truth can only be found in one place: the code. Only the code can truly tell you what it does”

2.1 Core Principles for Writing Clean Code

The foundation of clean code is a set of principles that guide a developer’s choices at every level, from naming a variable to structuring a system.

  1. Meaningful Names: Names in code should be intention-revealing. They should answer why it exists, what it does, and how it is used.

    • Use names you can pronounce.
    • Use searchable names.
    • Avoid encodings (like Hungarian Notation or member prefixes like m_).
    • Class names should be nouns; method names should be verbs.
  2. Functions: Functions are the first line of organisation in code. They should be small and do one thing.

    • Small!: The first rule is that they should be small. The second is that they should be smaller than that. Ideally, a function should be easily visible on one screen without scrolling.
    • Do One Thing: A function should have a single, well-defined responsibility and should not be divisible into sections.
    • One Level of Abstraction: The statements within a function should all be at the same level of abstraction, following the “Stepdown Rule” where code reads like a top-down narrative.
    • Few Arguments: The ideal number of arguments is zero, followed by one, then two. More than three arguments should be avoided. Flag arguments are a sign that a function does more than one thing.
  3. Comments: Comments are not “pure good”; they are, at best, a necessary evil. The best comment is the one you found a way not to write by making your code more expressive.

    • Comments do not make up for bad code. Rewrite the code.
    • Avoid redundant comments, journal comments, and commented-out code.
    • Good comments include legal notices, warnings of consequences, and TODO notes.
  4. Formatting: Code formatting is about communication. A consistent style makes code easier to read and understand.

    • The Newspaper Metaphor: A source file should read like a newspaper article, with high-level concepts at the top and details increasing as you scroll down.
    • Vertical Formatting: Use blank lines to separate concepts. Keep related code vertically dense.
    • Horizontal Formatting: Keep lines short. Use horizontal whitespace to associate related items and disassociate weakly related ones.

Bad code can bring a company to its knees. As the mess grows, productivity approaches zero. Adding new developers to a messy project only makes it worse, as they don’t understand the original design intent and are pressured to add more mess. This often leads to a “Grand Redesign in the Sky” which frequently fails, creating a vicious cycle. The only way to go fast is to stay clean.

“Leave the campground cleaner than you found it.”

If every developer checks in their code a little cleaner than when they checked it out - by improving a variable name, splitting a function, or removing a small bit of duplication - the system’s quality will steadily improve over time. This continuous improvement is a core part of professionalism.

2.2 Handling Structure and Errors

Beyond the basics, clean code involves robustly designed structures and error handling mechanisms that don’t obscure the primary logic.

  1. Objects vs. Data Structures:

    • Objects hide their data behind abstractions and expose functions that operate on that data.
    • Data Structures expose their data and have no meaningful functions.
    • This creates an anti-symmetry: it’s easy to add new functions in procedural code (data structures), but hard to add new data structures. In OO code, it’s easy to add new classes (types) but hard to add new functions. Choose the right tool for the job.
  2. Error Handling: Error handling is important, but if it obscures the program’s logic, it’s wrong.

    • Use Exceptions Instead of Return Codes: Returning error codes clutters the caller and leads to nested if statements. Throwing an exception separates the error-handling logic from the main program flow.
    • Write Try-Catch-Finally First: This defines a scope and forces you to consider what happens when an operation aborts, ensuring your program remains in a consistent state.
    • Don’t Return Null: Returning null creates extra work for the caller, who must check for it. A missing null check can cause the application to fail.
    • Don’t Pass Null: Passing null into methods is even worse. It creates a burden on the method to handle it, often leading to more NullPointerExceptions. Forbid passing null by convention.

A method should only call methods on:

  1. Itself
  2. Objects passed as parameters
  3. Objects it creates
  4. Objects held in its instance variables

This avoids “train wrecks” like ctxt.getOptions().getScratchDir().getAbsolutePath(). Instead of asking an object for its internals, you should tell the object what to do (e.g., ctxt.createScratchFileStream(classFileName)). This principle helps to reduce coupling between modules.

2.3 Other key ideas

Test code is as important as production code and must be kept clean. Dirty tests are hard to maintain and will eventually be abandoned, leading to code rot. Clean tests are readable and follow the F.I.R.S.T. principles:

  1. Fast: They should run quickly.
  2. Independent: Tests should not depend on each other.
  3. Repeatable: They should pass in any environment.
  4. Self-Validating: The output should be boolean (pass/fail).
  5. Timely: Write tests just before the production code that makes them pass.

This is enforced by the Three Laws of Test-Driven Development (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.

The first rule of classes is that they should be small. The second rule is that they should be smaller than that. Size is measured by responsibilities.

This is guided by the Single Responsibility Principle (SRP), which states that a class should have only one reason to change. High cohesion - where methods and variables of a class are co-dependent and form a logical whole - is a sign of good design. When a class loses cohesion, it’s a signal to split it into smaller, more focused classes.

Writing clean concurrent code is very difficult. To manage its complexity:

  1. Separate concurrency-related code from other code (SRP).
  2. Limit the scope of data. Encapsulate and restrict access to shared data. Use synchronized sections sparingly and keep them small.
  3. Use copies of data to avoid sharing where possible.
  4. Know your library: Use the thread-safe collections and executor frameworks provided by your language (e.g., java.util.concurrent).

2.4 Key Heuristics Checklist

  • [Names] Does the name reveal intent? Is it pronounceable and searchable?
  • [Functions] Is this function small? Does it do only one thing? Does it have three or fewer arguments?
  • [Comments] Does this comment add value, or could the code be made more expressive to eliminate it? Is the comment accurate?
  • [Formatting] Is the code formatted consistently according to team rules? Does it read like a well-written article?
  • [Structure] Does this class have a single responsibility? Is it highly cohesive?
  • [Error Handling] Are you using exceptions? Are you avoiding returning or passing null?
  • [Duplication] Have you eliminated all duplication? (The DRY Principle: Don’t Repeat Yourself).
  • [Tests] Are the tests clean, fast, and independent? Is there a test for every piece of production code?

3 Summary Video

4 Practise

A great way to practise the principles from “Clean Code” is to perform a refactoring exercise.

  1. Find a function or class in one of your current projects that you know is messy, long, or difficult to understand.

  2. Before changing anything, ensure you have a suite of tests that cover its current behaviour. If not, write them first.

  3. Go through the Key Heuristics Checklist above. Apply one principle at a time.

    • Rename one variable to be more descriptive. Run the tests.
    • Extract one small part of a long function into a new, well-named function. Run the tests.
    • Remove one redundant comment. Run the tests.
  4. Continue this process of making small, incremental changes, running the tests after each one, until the code is noticeably cleaner.

5 Learn More

Back to top