Photo by Scott Umstattd on Unsplash
Today I wanted to talk about clean code since we closed last week our SOLID series, I thought it would be perfect if I continued writing about clean code.
What is Clean Code?
Clean Code is Code that can be read and directly understood. Reading code that turns out to be what we expected before reading it -> Simple and "Boring" code.
The only photo that illustrates what I mean(in my opinion :p)
"Wtf metric should be zero"
And what a code does is encapsulated inside functions, one of the most important aspects of it, in my opinion.
But since we are talking about functions, I want to briefly explain why we write functions:
To decompose abstraction on so many levels and split the logic.
Function Rules:
1 - Small:
By small I mean that the function ideally shouldn’t exceed 10 lines, 20 in worst cases.
The code should be small so that we can easily understand the intent of the function by just reading it.
This is important especially when we are talking about big projects, where we have a lot of modules. In these projects, it is important to keep track of the logic and code in small easy to understand blocks, so that we don’t end up in total chaos.
Another Point, Smallness implies that blocks within if statements, else statements and while statements should be ideally one line long ( most probably a function call --> and it should document the intent ).
2 - A function should do one thing: and should do it well!
But what does one thing mean: if the code in the function is at the same level of abstraction
(A level of abstraction is just a metaphor used to hide the tiny details of a subsystem. For example: let's say we have a part of our system that creates a list and then increments its values by ten. Creating the list is a level of abstraction and incrementing the value is another level.)
then the function is doing one thing.
Or like uncle Bob says
"if we still can extract more from a function then that function is doing more than one thing".
In my words, if a function is reduced to the lowest then it’s doing one thing.
3 - We should use descriptive names:
If it’s easy for us to name a function, in a way that we can document its intent, then our function is most probably well-written and easy to understand. If it’s the other way round, then you should probably review the function that you wrote.
But sometimes, to document a function, its name can be too long, is that okay? Some people ask. Well, my answer to that is that before in the old days, people had to name functions using only 4 letters. That’s why I would say we should embrace the possibility that we can name our functions the way we want. Let’s avoid abbreviations.
"It is better to take many small steps in the right direction than to make a great leap forward only to stumble backward."- Louis Sachar
and that is if I want to optimize time, and not spend on thinking about function names, this would only slow me down in the next days when I try to figure what the names that I wrote stand for.
4 - Not a long number of arguments, the more arguments the more complication:
Personally, I would put it like this. Deliver only the needed arguments. And if the arguments are not on the same level of abstraction then the function we're calling is doing more than one thing.
Let's say I have a car that consists of 4 wheels, 5 chairs and a Steering Wheel.
Let's suppose that our chairs are composed of springs.
Then a call to the function car like this is to avoid :
Car(wheel, chair, spring, steeringWheel)
because I would initialize two things (not at the same level) the car which is the higher level and the chair (lower level).
I would rather call that function like this :
Car(wheel, doneChair, steeringWheel)
Please notice that we didn't deliver something that is not necessary there and that would break another rule that we could have respected.
Some People say: If we pass a boolean to a function then we're clearly stating that this function is doing more than one thing.
But this is not always true. Because sometimes we're maybe calling two different functions or just passing different params.
5 - No side effects:
What is meant with side effects is not what most people say, it doesn't have to be something hidden or unexpected, it can be but it is not the definition.
A side effect is something changing somewhere else. In computer science, an operation, function or expression is said to have a side effect if it modifies some state variable value(s) outside its local environment. And this type of functions ( with side effects ) are not testable, because they don’t have arguments to be tested.
What do I mean by that?
String carString = "Wheel Chair";
char[ ] carArray;
public void carArray () {
carArray = carString.toCharArray();
}
Now can we test this method? If yes? What would we test? With which arguments? The method doesn’t return anything, doesn’t accept arguments...
public char[ ] carArray () {
return carString.toCharArray();
}
This one looks way better.
Conclusion:
Like I always say, these are not rules that we have to stick to, and make us judge each other. Sometimes bad code (not clean code) is necessary to keep some minimum level of understandability.
Before caring about code we should care about each other. Do not refactor if that makes the code more difficult to understand.
Sources:
https://en.wikipedia.org/wiki/Side_effect_(computer_science)#:~:text=In%20computer%20science%2C%20an%20operation,the%20invoker%20of%20the%20operation.
https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29
“Clean Code”, Robert C. Martin
Top comments (0)