Writing clean code: Functions
Clean functions are the first line of organisation in any program.
In this article, we are going to learn about the importance of writing clean functions when writing code. Previously we have learned about the importance of Naming in writing clean code.
When you start learning how to code your main focus is writing code that works correctly for the problem you are solving. Yes, code needs to work correctly and that is a good place to start learning. However, in a real work environment, you also want to ensure you write clean code.
Clean code is readable, extensible, changeable, and maintainable. These are all important qualities for code to have.
In many professional settings, you are not writing code in complete isolation. There are other people you work with on a team. Your teammates need to be able to easily understand the code you write. They need to understand what it does, how it works, and why it was written that way. It may also be your future self coming back to code you wrote in the past and you will also need to easily understand your code.
Code is read more than it is written. To write code we are constantly reading it. So even if readable code is harder to write we should invest time to do it instead of writing easy unreadable code. Writing clean code is an important skill for any professional developer.
Functions are the basic building blocks of any system. They are the verbs of a system, while classes are the nouns. We want to write our functions in such a way that they are easy to read, understand, maintain and refactor.
There are some recommended rules for writing good clean functions in code. Let us discuss these rules for the rest of this article.
Functions should be small
“The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.” — Robert C. Martin
“Coding like poetry should be short and concise.” ― Santosh Kalwar
One of the simplest ways of writing clean functions is to keep them small. Small functions are easy to understand and their intention is clear.
Functions that have hundreds of lines of code or more are usually complex and messy functions. There is no hard defined rule of how small functions should be. Generally if you can keep your functions to a maximum of 10 to 20 lines of code they are going to be easier to understand. Anything longer than that can likely be split into multiple functions.
Function arguments
Another way to ensure you are writing clean functions is to keep the number of arguments to your functions small. If a function has too many arguments it is either doing to many things or there is an encapsulation missing.
If the function is doing too many things you should split it into multiple functions that each receive the arguments they require.
If the function is doing one thing but requires many arguments then an encapsulation of a concept is missing. Extract the arguments into a class and pass in the object as the argument to the function.
There is no hard defined rule of how many function arguments are too many. Generally if you have 3 or more function arguments, check if your function can be cleaned up with the suggested solutions above.
Code blocks and indenting
The rule of keeping our functions small also implies that we keep the code blocks inside if, else and while statements minimal, preferably up to one line and that one line should be a function call. Extract the logic inside the code block into a function. Similarly, we should avoid nesting such structures inside functions by moving the nested logic into another function.
You can also keep your function code clean through indentation. Indent your code in a neat way that subscribes to your team’s agreed standards. Make it easy to read.
The stepdown rule
The author's stepdown rule is very helpful for writing easily readable code. According to this rule, you should write functions in such a way that function calls should be followed by their definitions in the code below. In this way, while reading a code file, when you see a function call you don't have to scroll up to find its definition because you will find its definition down below while reading through the code at a constant pace.
Do one thing
“Functions should do one thing. They should do it well. They should do it only”.
“DOTADIW: Do One Thing and Do It Well” — Unix philosophy
The functions you write should apply the Single Responsibility Principle. One function must do only one thing. If a function is doing more than one thing, the function should be split up into more functions.
Use descriptive names
“You know you are working on clean code when each routine turns out to be pretty much what you expected.” - Ward Cunningham
The name of a function should answer all the big questions. It should tell you; Why it exists? What does it do? How it is used? If a name requires a comment, then the name does not reveal the function’s intent.
A function‘s name should explain what exactly it is doing. Functions with names like do()
, action()
, for example, are not very useful, because we don’t know what their reach is.
A few examples of good descriptive function names: createNewLoanApplication()
, getAllCustomers()
and readFileToStream()
.
Remove duplicate code
DRY - Don’t Repeat Yourself
Make every attempt to prevent duplication of code. Duplication of the code is bad since it means that if you need to change a concept, there is more than one place to change things. If you forget to change other places it will easily result in bugs in your code.
There is a nice rule to maintain DRY - If this is the first time, code it. If this is the second time, copy it. If this is the third time, extract it to a function.
Avoid side effects
Side effects are mutations or actions that happen in our code environment that we cannot make an account of. There should be no hidden changes that a function name does not suggest.
An example is a readFile() function that reads the content of a file then deletes that file without notifying the user.
Side effects can lead to a lot of problems in our code and some bugs that are difficult to debug.
Remove dead code
Dead code is code that is unused or has no execution path that leads to it. It is usually never updated. It causes confusion to developers working in the codebase. They are unsure whether the code is used or not. Any time you find dead code in your codebase delete it. If you ever need it you can find it in your source control history.
Command query separation
A function should either ask something or do something, but not both. This creates confusion for your colleagues and your future self.
Either your function should change the state of an object, or it should return some information about that object. Doing both often leads to confusion.
Closing thoughts
The basic building blocks of any system are functions. When you write your code the functions should be clean and clear.
This article was about writing functions in such a way that they are easy to read, understand, maintain and refactor.
By following the above principles and practices your functions will be descriptive, clean and beautifully organised.
References/Resources:
Clean Code: A Handbook of Agile Software Craftsmanship by Robert C Martin.