Notes I made during reading Clean Code by Robert C. Martin

Posted on Nov 16, 2022

Not so many books are recommended by so many generations of software developers as Clean Code by Robert C. Martin. And after reading it I can just agree on this.

This book can make you a better developer or if you already have professional experience give you advices to strengthen and encourage your existing techniques.

Some of the things written down may seem outdated or you may not agree on them (which is also the intention of the author), but it will give you a guide and frame for coding on which I think every developer will agree.

I can fully recommend it. Some of the chapters seem to be very trivial, some not so interesting but all in all I think everyone can get something out of there. And on every page is one useful statement.

I wrote down some notes with things I considered important or which led me to think more about my current skills and how to develop them.

Here is the uncommented list.

Functions

  • Outgoing arguments should be generally avoided. If a function has to change the state of something, it should change the state of its owning object like: report.appendFooter()
  • If the above is not possible, then keep in mind, that readers expect arguments to be inputs and not outputs. So: appendFooter(s) should append the footer on s and not append s as the footer.
  • When you need to check the signature of a function to know what it does, this is handled as a cognitive break. Such un intuitive functions should be avoided.
  • Flag arguments should be avoided. They provide more than one outcome of a function.
  • Selector arguments which lead to different behavior based on the selector should also be prevented. If a selector argument of a function is driving and changing the behavior of a function, then it should be split up into two or more functions. Like:
public int calculateWeeklyPay(boolean overtime) {
    if (overtime) {
        // different behavior when overtime is true
    }
}

When calling calculateWeeklyPay(true) we will not directly know what this means without checking the signature of the function.

So it should be split up:

public straightPay() {
    ...
}

public overTimePay() {
    ...
}
  • Temporal couplings should not be hidden. When needed they should be exposed, even if it makes functions more complex. If function A needs to be called before function B, then you should expose it by making the result of function A necessary for function B.

Boundaries

  • Don’t pass instances of third-party interfaces as they can change. Wrap/encapsulate them in a class. E.g. when the following map is passed around your service:

Map<Sensors> sensors = new HashMap<Sensor>()

Should go into it’s own class.

public class Sensors {
    private Map<Sensors> sensors = new HashMap<Sensor>();

    public Sensor = getById(String id) {
        return (Sensor) sensors.get(id);
    }
}

Tests

  • Learning tests are good to learn third-party apps. They also provide a way for the future to test if the library is still working as we use it after a version update.
  • Tests need to change with the production code. So it is also important to keep them clean.
  • ‘Without tests every change is a possible bug.’
  • Domain-specific test language with functions that make use of the API will lead to better readability and more expressive test.
  • Use tests for a ‘documentation by example’.
  • Test exhaustively near bugs.
  • Don’t skip trivial tests: They add much value in regards of documentation.

Classes

  • Try to describe what a class does. If you use words like ‘if’, ‘and’, ‘or’ or ‘but’, then that’s a sign for too many responsibilities of a class. Also the name can be a good hint for example when it is called a Manager, or Processor.
  • Always keep the Single Responsibility Principle in mind: A class should have just one responsibility and one reason to be changed.
  • Keeping things clean is as important and the same business as making things work!
  • Refactoring too many responsibilities: Check which responsibilities a class has, cluster them together and move the responsibilities into other classes.

Concurrency

  • Clean Code principles also apply to concurrent code in it’s own way.
  • Concurrency-related code should not be applied directly into other production code and have it own life cycle of development, change and tuning.
  • When there is concurrent access on data fields, then it is a so called critical section. Restrict the number of critical section in the code. Also: Limit the data which can be accessed concurrently.
  • Each thread should exist for it own and not share data with other threads.
  • Keep in mind that there thread-safe collections and libraries exist (like the java.util.concurrent package).
  • Synchronized sections should be as small as possible.
  • Advise: Encourage task swapping by running more threads than processors or cores to have a higher chance of hitting a critical section or deadlocks.

Smells and Heuristics

  • Most often clean code is about partitioning code into smaller parts.
  • Hide data, hide utility functions, hide constants and temporaries. Keep the interfaces of functions as small as possible. Good code can do much with less!
  • If you do something a certain way, then do all similar things this way. This leads to the principle of least surprise!

More interesting stuff on concurrency

  • The synchronized keyword vs. the Atomic... classes of Java 5 (can be found in Appendix A: Concurrency). The synchronized function always acquires a lock which leads to a worse performance in comparison to the Atomic classes.
  • Deadlocks are caused by these four conditions and if we want to solve or prevent deadlocks, we may need to fix or prevent one of these reasons.
- Mutual exclusion
- Lock & wait
- No preemption
- Circular wait

Want to know more?

Keep on reading and choose one of the related articles. You can also check the home page for my latest thoughts, notes and articles.