This is a 6-part series article on Clean Code. I used (YT) video from Robert C. Martin aka Uncle Bob as a reference. If you want to deep dive in each topic discussed here, please refer to the video and/or the book 'Clean Code by Robert C. Martin'.
Now let's get started.
Why should you care about Clean Code?
Clean code act like a well-defined communication protocol when you are working on a project along with your teammates or on an open source project. You want to write code which is not just understandable by machine but also by your fellow programmers. Computer can understand 10k lines of code(if written syntactically correct ) even if you put it in a single line with no spaces but I bet your peers will have a hard time understanding it. Clean code makes debugging, reading codes(which you will be doing more than writing in Software Engeering), maybe teaching?, etc a lot easier and more efficient.
What is Clean Code anyway?
Programs which is simple, direct, elegant and efficient. Clean code does only one thing and does it well. No surprises i.e when you are reading it, it should only do what you are expecting. It should look like well-written prose and by someone who cares.
Rules of function
A function should be small from 4 to 20 lines. It must do only one thing. How do you know if a function is doing more than one thing? If you see a function and you can extract another function from it, that original function was doing more than one thing. You should keep extracting functions from one another until it stop making sense to extract any further. Each functions should have a proper name and it should be a part of appropriate classes(functional programming?) and modules.
However having a hard requirement of a function of line between 4 to 20 can be difficult because sometimes a function which is doing only one thing could grow more than 20lines. And also, if you are on a line of code which is result of call of 10 different small functions, it will be hard to backtrack when you are debugging or trying to understand. so, keep it balanced and try to follow one function - one job rule more strictly rather than length of code rule.
Function arguments/parameters
How many arguments function should take?
- A function should not take more than 3 arguments(Soft rule). Anything more than three should be an object.
Passing a Boolean to functions should be avoided if possible. Why?
- Because when you are passing a boolean to function, most likely there will be a
if
/else
statement in that function and code in theif/else
block could be extracted out as two different functions.
Avoid switch
statements
switch
statements are great for small, finite and well-defined sets cases eg parsing commands or options but they can become a painful dependency management problem if you are calling different modules in your switch statements.
Lets understand this with an example, Shapes.
We have shapes like triangle, rectangle, square, circle, etc. We want to perform some actions based on shapes like rotate, draw, stretch, get number of sides, etc. If we have bunch of switch
statements floating around for each actions, what will happen if we want to add new shape? Exactly, find all the switch
statements and add actions for this new shape. And what will happen if we want to add an action which can be only true if two or more conditions satisfy? Also what does rotate or number of sides means in case of Circle? You see the problem. For such cases, Polymorphism works best as it follows Open-Closed rule.
Open-Closed Principle - Software entities (such as classes, modules, and functions) should be open for extension but closed for modification.
No Side-effects
A side effect in programming is any change a function makes outside of returning a result, such as modifying a variable, updating a file, printing to the console, or interacting with the outside world.
Some example of side-effect functions can be open()
and new
in C++. These side-effect functions comes in pair like open/close, new/delete, acquire/release, etc.
Does that mean we should not use these functions? Ofcourse you are going to use them if you write in C/C++ but the important point here is to make sure you handle them properly. That's why some programming languages comes with Garbage collection to avoid memory leaks but still you need to manage a lot of things by hand for most of the cases.
But side-effects are not only limited to language's built-in methods/functions, we can very easily introduce them and suffer while debugging or when trying to understand someone else's code. For eg, our int userId
might very easily insert a user id in database and return it. How to avoid it? Ans: Command and Query Separation.
Command and Query Separation (CQS) is a software design principle which states that a method (or function) should either perform an action (a command) or return data (a query), but not both.
Okay that's that but what can you do to enforce proper resource/transaction management like reading from a file and closing it, connecting to a database and closing the connection, serving a request which can change two or more states, etc? Ans: Exception handling
You can have a finally
block which will release the resouce or can do cleanup for you if you don't want a partial completion of the request and so on. But avoid having bunch of code before the try block and code after except/finally block. The function/method which has this exception handling logic should only call functions/methods which can throw. Also, Never ever use nested try/catch blocks.
DRY
Don't Repeat Yourself
Pretty self-explanatory ig? Just try to avoid having repeated code all over the place. Move repeated codes in functions(pass arguments if needed) and modules.
Test your code
How would you know if the code you wrote is not going to break? Well, I tested the the feature and it works.
But what if it breaks or behave differently on different sets of arguments/situations? What if a simple arithmetic operation can overflow if correct datatype is not choosen or you got more strings in your fixed sized string array? The small or big changes/additions to the software can break the system or can be a bottleneck in the pipeline and you might have to wake up at 3am to fix it or backout and can impact clients.
To avoid these kind of situations, we write tests to test our code before shipping it to clients. There are many types of tests such as unit, integration, system, A/B, etc. Although more coverage means more confidence in your code, having unittests and integration is more common you will see in most of the codebases. So, try to write good, robust and good coverage tests.
--
That's it for this article. Lmk if something is incorrect or missing. Thank you.
Top comments (0)