Key takeaways of Robert C. Martin’s book “Clean Code”

Since I see writing code as a craftmanship, I’m also very keen on writing high quality code. In the beginning it might take more time to write your code with a high quality, but in the end this will pay back. For sure the bigger a project becomes, more time it will take to add or change functionality or fix bugs in poorly written code.

When it comes to high quality code, of the aspects that come to mind is that it’s clean code. Clean code helps your team to write and maintain in an efficient way your code base. What properties must programming code have to become clean? Robert C. Martin (a.k.a. Uncle Bob), describes in his book Clean Code – A Handbook of Agile Software Craftsmanship” a number of aspects clean code could have. In this blog, I want to discuss the key takeaways I found within this book.

Choosing the right name

When reading code, you want that humans have the less issues with understanding your code. The way to do this, is by giving as a starter the methods you’re using, meaningful names. The name of a variable, function, or class, should tell you why it exists, what it does and how it is used. Use also names that are easy to remember and to talk about, like GenerateNewAccount(). When discussing an issue or design with colleagues, this will ease the conversation when talking about a certain method.

For example

Weight newWeight = weight.add(10)

The example above doesn’t say anything. Will add increase the weight with 10 kilograms or 10 grams? From the name of the method you can’t simply tell.

Names that can be easily searched for

Eventhough modern IDE’s like Intellisense in Visual Studio helps you to find for example the usage of a variable, it is better to use names that can easily been searched for in a text-editor. A variable like i is harder to find within your code. Take for example also the case with bindings within Visual Studio. Intellisense won’t find names of members here. By using an easy to search for name, you also quickly will find where a member is used within a binding.

Don’t include type information in the name

Beware of not putting the type of the variable in the name of it. If you use for example the string phoneString and want to change the type later on, you also have to rename the variable. If you forget this, this misinformation will even lead to problems.

Using nouns

When it comes to choosing a right name for a class, use a nouns. Avoid verbs like for example ReadFiles.

Designing your functions

The previous part went over the starting point of writing a function, giving it a proper name. Now we go more into detail what to think of while designing the internals of your function and how it will communicate with the outside world.

First of all a function needs to be small. It is better to split up a function into several sub-functions if a function becomes to big.

Also a function should be doing one thing. If your function is doing more then one thing, that most of the times can easily being derived from its name.

Use of arguments

Adding arguments increases complication to read. Take the following example:

employee.IncreaseEuroSalary(200, 3)

What is ment with the first parameter, what with the second? You have to look into the names of the parmeters of IncreaseEuroSalary to understand their meaning:

public void IncreaseEuroSalary(int euros, int nrOfMonth)

So it seems it will only increase the salary for the 3th month? Some may then argue to use this construction:

employee.IncreaseEuroSalaryForMonth(200, months.March)

This is some better, but it still violates the fact this method is doing two things, and is therefore still harder to read. So it’s better to use arguments sparse.

Couplings

If there is a hidden coupling between functions, like a certain function is called before another one, make it explicit. For example you have these function calls:

DoA();
DoB();
DoC();

Obvious DoA should be called before DoB and so on. You can make this explicit by doing this:

var resultOfMethodA = DoA();
var resultOfMethodB = DoB(resultOfMethodA);
var endresult = DoC(resultOfMethodB);

Although this adds some complexity in the sense we’re using an additional parameter, but to make this coupling more clear and accidently change the order, is a good thing.

Flag arguments

Also adding flag arguments to a method is a warning sign. This means the method does actually more than one thing. A good example of a code-smell is the following method, which calculates the heath of all rooms within a house:

public bool CalculateTotalHeat(bool? excludeLivingroom = false)

So it seems we can exclude the heat from the living room by passing on a boolean value. Besides this also requires you to think of an invariant logic, it also needs you to think about what kind of behavior I want from this method. In fact it are two separate methods combined into one.

Better is to split this method into two separate methods, which state what they do:

public bool CalculateTotalHeatHouse()
public bool CalculateTotalHeatHouseExcludingLivingRoom()

The long method name is no problem actually, the name of the method states now what it does.

Side effects

Prevent a function or method to have side effects. A side effect can be when it changes for example a member of a class, while the method states it only retrieves the value of this member. In fact something is being done what is not being described. This comes back to the point that a methods has the right way and describes properly what it does, which we mentioned at the beginning of this section.

Command query separation

Either do something or return something, not both. It will often lead to confusion. Take for example this:

public bool SetValue(string id, string value)

Will true be returned if the provided value is the same value as id currently holds? Or does it return true if the value has written to disk? Or does it return true when it didn’t yet exists and is created as a new value? It can’t be derived from the information that is presented over here.

It will also lead to constructions like this:

if (SetValue("name", "John"))
{
 ...
}

Assume the method returned true if the id didn’t existed yet. In this case the following construction would make more sense:

if (IdIsUnique(id, value))
{
   SetValue(id, value);
}

Law of demeter

A module should not know about the innards of the objects it manipulates. Objects hide their data and expose methods to manipulate this data. Methods should not invoke methods on objects that are returned by any of the public functions: talk to friends, not to strangers.

In fact you can simply remember to use only one dot. So the following syntax brakes this law:

persons.GetAdults().Get("Joe").SetAge(56);

In fact the GetAdults exposes a structure, that then is used to get Joe. After getting this instance we change the age of Joe. The function now holds a lot of information, how to navigate thorugh a lot of different objects. Is ok to go true a cain if the chain consists of objects. If they are datastructures it is not ok, since datastructures should hide their

This also could lead to what Martin Fowler calls feature envy. In this case a method of a class reaches into another object to get the data on which it operates.

Exceptions and error codes

It is better to use exceptions than provide error codes. This also prevents breaking the Command query separation principle.

Java uses unchecked exceptions, this means you have to catch all the exceptions a call can throw or re-throw the exception. The book states that this will break the Open/Closed Principle, due to the fact when a method changes and throws other kinds of exceptions. When one method re-throws this exception, you can end up in changing a long caine of depending calls that either must re-throw or catch the exception.

Don’t return null in case of an error. This will end up in a large number of null-checks in your code. Better to throw an exception.

Boundaries

By writing a wrapper around an API-interface, you prevent that changes in the API will affect your code. In case of a change, you only have to change your wrapper. Writing a unit test around the API will provide a test before integrating a new version of the API (see also learning tests).

Also you can tailor your wrapper in such a way it only exposes methods and getters/setters you requir a user can access.

Testing

Learning tests

The book refers to Jim Newkirk calling learning tests, tests to explore the understanding of the third-party code. In theses tests we write little code snippits of code that uses cases we might put into our code. By writing these snippits we get familiar with the code, and find out how to get the API working.

It also helps you to do in-take tests, in case a new version of the API arives. In this case you can run your test suite to see if there are any changes within the API with respect to the expected behaviour.

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 (note not compiling is also failing)
  3. You may not write more production code than is sufficient to pass the currently failing test.

The first two laws prevents us from writing code before getting the specifications clear. We write the unit test what we expect the code will do. In the beginning this can be a constructor, since there is no class with a constructor yet, it probably will fail.

The third law describes writing code as less as possible to pass the test. In the case of our unit test using a constructor, this can be as simple writing the constructor of the class.

Also don’t rely on your intuition. Create for each boundary condition a test, even though you think a certain case won’t fail. An small coding error is in a small corner.

F.I.R.S.T.

Clean test follow the following five rules:

  • Fast
    Test should run quickly. Otherwise people tent not to run them.
  • Independent
    A test should not depend on another test.
  • Repeatable
    Each test should be repeatable in any environment.
  • Self-validating
    The test itself should determinate if the test succeeded or not. You don’t have to compare output yourself.
  • Timely
    Tests are written in a timely fashion, before writing production code. This prevents that you’re code isn’t testable.

Classes

Designing your class

Just like a method, a class should do only one thing. Furtheremore when it comes to designing your class, be aware of respecting the SOLID-principles.

Writing object oriented programs, has the concept that the higher level base class concepts can be independent of the lower ones. This means, base classes should know nothing about their derivatives.

Duplication of code can often indicate a missed opportunity of abstraction. The duplication could be moved in a subroutine, used by several other derived classes. If someone derives from your class, they can easily reuse the method you already created and tested. This will speed up development. Also removing duplication, means you have to fix an issue at only one place.

Layout

For readability it is better to put private functions just below where they are used for the first time. This way the reader can easily find the function that is called from within another function.

Also when using local variables, you want to declare the variable just above the first usage, and having a small vertical scope.

Remove old code that isn’t used anywhere. If you leave it there, it probably won’t be maintained and begin to smell.

Negative conditionals

Try to avoid negative conditionals, since they are harder to grasp. So for example the following example is better

if(timer.elapsed)

then this one

if(!timer.counting)

Conclusion

The book “Clean Code” is a dense packed book, containing a lot of best practices. Also, like the authers suggest, these are rules the others will think will bring better quality to your code. It is a book that you will read, try out and grab back again to read again and again. In the end you only will code clean by excersising.

Besides these best practices, which are described indepth also provided by the rationale behind it, it also contains a couple of chapters that will take the reader by the hand applying those practices. A piece of code is taken, and step by step this is refactored using the described practices.

Posted in Best practices, Books by Bruno at April 14th, 2021.
Tags: ,

Leave a Reply