Clean Code — Part 1
In an ever changing software landscape, languages come and go with whatever is fashionable at the time, but certain fundamentals of coding remain the same regardless of the current language. Anyone who has been coding for a significant length of time, will have undoubtedly inherited badly written code, where most of your time is spent attempting to workout what the function is meant to do. Then you make a change, and you get an unexpected result. In addition, you may have even been guilty of leaving bad code yourself. Have you ever had a tight deadline, been in a rush to finish the project, and sacrificed clean code to finish the functionality for the deadline?
I recently read a book and it said the “difference between good code and bad code could be expressed as ‘WTFs per minute’”. I have to agree, coding big projects which have functionality continually added and removed, it can be a frustrating enough task trying to keep everything working, without having to deal with badly written code into the equation.
The main benefits of clean code are; it significantly reduces the amount of time spent trying to deduce the intention of the code. Once you know the intention, you can easily check if it does what its supposed to. Because you can see the intention, updating and debugging code becomes a lot easier, you can begin to see how changing code is going to affect other functions and classes. From my own experience of working a big project, and returning to it once every six months, you start to forget what the function is meant to do, what else does it affect? If you have clean code it’s really easy to see what it’s meant to do, and developers who have never worked on the project can come in and quickly start working on it.
Lets get started with what should be one of the easier changes to make, variable and function naming conventions. Code is a lot easier to read if the variables and functions tell you what they do/represent. Consider the code in Example 1.
See left is an example of bad code. It’s not the worst code. You can still follow the flow, but you can’t be totally sure what the intention of the code is. What does ‘Thing’ and ‘OtherThing’ represent? How does changing either of these classes affect the rest of the code? You would need to add comments for anyone other than the original developer to be sure of what the code is doing. When naming variables, functions and even classes, giving them self-explanatory names makes code a lot easier to read and understand.
In Example 2 (left) even at first glance, you can get more of an idea what the intention of the code is. You can see that there is a lamp and a light switch, the light switch turns the lamp on and can also close the curtains. You can clearly read how the code is making decisions, and each function is really simple. There are no convoluted exits to the function. Which means you, as the programmer, don’t have to try and work out which exit the code is going to take. All that extra clarity for just over 10 lines of code, and in this particular example, its actually quicker to make Example 2 as you don’t need to make up and remember variable or function names because they are intuitive and flow.
In the above examples, because it is a really simple program, the functions are already very small, but these are about the length most of your functions should be. Functions should be on average no longer than 20 lines, and never more than 100 lines of code. If you remember that a function should only do one thing, very few ‘one thing’s will take 20 lines of code to accomplish. I can’t think of any functions that are 100 lines of code, or more, that can’t be broken down into several shorter functions. One particular project I have worked on, regularly had functions that were over 200–300 lines of code, some had more! There was duplicated code all over the project. Obsolete code was left over from several updates ago, model attributes were manipulated in the controller, and many other bad habits, that made updating and maintaining a nightmare. When refactored with clean code, it was much easier to manage, as there were no nasty surprises inside functions and everything was where you expected it to be.
A recent discovery I have made about functions relates to conditional statements, conditional statements should call another function as the outcome, it should not perform actions inside the statement. This actually makes sense, the condition is one “thing” and the outcome is another “thing”. See Example 2, lines 35 to 39. In the if statement, a function is called instead of setting the lamp status (_is_on happens to be private, but calling a function is the preferred method even on public attributes).
One of the hardest things I find to implement in clean code, is the number of arguments passed to a function. Ideally, you should pass no arguments (niladic), one (monadic) or two arguments (dyadic) are ok, but you should try not to use three arguments (triadic) where possible, and you shouldn’t ever use more than three arguments (polyadic). When refactoring the big project mentioned above, this was quite a challenge, as quite a few functions has four or more arguments being passed.
And now my least favourite part of clean code. Some programmers think you can’t have clean code without testing. Personally I think you can go overboard with testing. Some tests feel to me like comments, you’re just doing it for the sake of it. Do you really need to test a model which only has two or three attributes? I do think that functional tests are very useful to a developer. It saves so much time when functional tests have been well written. They are definitely worth the time it takes to write and maintain, (and man does it take a long time? Phew!). They help you to make sure that your changes haven’t affected other parts of the system, which in turn keeps the end users happy. (read: quiet life).
When I was researching clean code there was one thing that made me celebrate, “comments, you shouldn’t need them”. Personally I have always been a fan of “ninja comments”. If you feel the need to add comments, you could probably write the code better. What can you really express in a comment that you can’t express in a high level programming language? There are times where comments can be useful, if a certain function takes a long time to test, or to very occasionally highlight something. Almost exclusively, I only use comments to mark what I need to come back to, and always prefix the comment so its easy to find later. One of the main reasons for writing bad code is, because you are up against a tight deadline. If that’s the case are you really going to go looking for comments that need updating? So do your future self a favour and spend your time writing the code well and ditch the comments.
In conclusion, writing clean code the first time around makes your life a lot easier and anyone who takes over your code will definitely thank you. It doesn’t take any longer to write clean code, and if you think about it, you wouldn’t write a messy incomprehensible letter, so why write messy code? Keeping code clean helps, especially with big projects, which can soon get out of control, with messy code where bugs can quickly start to appear for no apparent reason. If you have inherited code, you don’t have to refactor it all in one go, even if you only clean up the function you need to update thats a start. At some point, the code is going to get a lot cleaner, and when you are allowed to refactor, half of the work will already be done.
The main points to take from this post is:
1, to give useful and relevant names to everything,
2, keep functions short,
3, don’t send too many arguments to a function,
4, write useful tests and
5, only add comments where absolutely necessary.
Hope you have enjoyed this post, check back soon for part 2.
Read more about clean code on our website…
Toru Interactive are a software development company from Northampton, UK.They build Websites, CMS Systems, Sales…www.toruinteractive.com
Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin (2008) (ISBN : 978–0132350884)