Why should anyone even care whether their code is 'clean' or not?
Indeed, the ratio of time spent reading versus writing is well over 10 to 1.
We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write.
― Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
I think he put it rather nicely.
So we know why we should care, but what exactly is clean code? What separates clean code from messy code?
This is the answer I prefer: "code is clean when it's easy to read, easy to understand, and it's organized in a way that makes sense to other people".
Now that we have a definition, what exactly can you do to clean up your code? I've outlined my best advice below:
Making your code easy to read
Whitespace is your friend, and too much whitespace is far better than too little
Text, in general, is much easier to read when it's generously spaced, but it tends to feel overwhelming when it's too dense. Dense blocks of text also make it harder to keep track of your position in the document.
To illustrate, here's some dense code:
import function from module
variable = function(argument)
variable = function(variable)
def my_function(argument):
do xyz
variable = my-function(variable)
And here's some well-spaced code:
import function from module
variable = function(argument)
variable = function(variable)
def my_function(argument):
do xyz
variable = my-function(variable)
See how some space makes it easier to read?
The length of any line in your code shouldn't exceed 80 characters
Your code will be easier to read in many short lines, instead of a few very long ones. There's nothing magical about the 80 character limit, but I find it a convenient rule of thumb, and anything much longer quickly becomes a pain to read.
Making your code easy to understand
Writing too many comments is much better than writing too few
Comments can sometimes feel bothersome to write, but whenever you write any code that is even slightly complicated, you should have a short comment before it.
The comment should explain what you were trying to achieve and if it's a very complex piece of code, briefly documenting why you chose that approach might be a good idea.
Long identifiers are better than cryptic ones
Sometimes, it feels hard to express the purpose of a variable or a function in one word, and writing a multi-word variable name can feel awkward.
But it's still better to use a long name that clearly explains the purpose of a variable than one that is short but cryptic.
Avoid nesting where you can(but keep line length in mind)
This:
if x and y and z:
pass
is easier to understand than :
if x:
if y:
if z:
pass
but this:
if black and white and blue and red and green:
pass
is harder to understand than:
if black and white and blue:
if red and green:
pass
Organising your code
As much as is reasonable, DRY(Don't Repeat Yourself)
When you find yourself repeating the same(or very similar) code in more than two different places, you would usually be better served turning it into a function and calling it twice instead. Your code ends up being shorter, and making future changes becomes much less of a hassle.
Side effects are evil, so use with (extreme) caution
Some functions accept parameters and return a value, and do nothing else. That's what an ideal function looks like. Impure functions cause side-effects: changing a global variable or class attribute within the body of the function, for example. Side effects are evil because they generally make debugging a pain in the behind: You end up having to scrutinize each line of code to understand where the heck the value of your variable is coming from.
Your functions should ideally do only one thing
Functions that have more than one job are usually much longer than they should be and tend to cause much confusion in the code you're calling them. To use an analogy, if you asked a friend to grab you a book and he returned with a shoe in his other hand, wouldn't you be really confused? It's the same idea with your functions, so please don't confuse the poor folks who end up using or changing them. Your function should do what its name says, and nothing more.
Summary
- Keep it short
- Space it out
- 80-character lines
- Write lots of comments
- Use good names
- D.R.Y
- Pure functions
- Simple functions
Putting it into practice
No one keeps an eight-step checklist in their head as you're trying to solve a problem or fix a bug, so how does anyone manage to write clean code? Simple. We don't, at least not at first. The answer is called refactoring, and it means changing code without affecting the output.
After you're done with one piece of code, stop. Take ten minutes to breathe. Look away for a little while. Then go over the code you just wrote, and fix any messy bits you find.
Over time, writing clean(er) code will become more and more of a habit, and you'll find progressively fewer mistakes when you refactor.
Further Reading
I tried to make this post as useful as I could, but a full, in-depth treatment would probably require writing a short book. Here's some extra content you'll find useful if you want to learn more about writing clean code.
Top comments (9)
Good TLDR, I would add two things tho:
Historically, this value comes from the fact that IBM punch-cards had 80 holes in a row. Because of this, our first terminal screens could display 80 chars on a line. We have great screens today and 80 chars is just too short, since one often have to sacrifice other principles of readability (i.e good naming) to respect this limit.
I find the sweet spot to be a hard limit of 120 chars a line, with a soft limit at 100.
Thank you for the additions!
I didn't know that about the 80 character rule, nice
A little repetition is much better than unnecessary complexity...fair enough
Clean Code is well isolated. Which means that the code exhibits high cohesion and low coupling.
If the code is unit tested, it means the code must be isolated to be unit testable.
Having unit tested code squelches spooky action at a distance behavior.
Having unit tested code also suppresses emergent behavior. And acts as developer pressure to abide by SOLID principles.
Having unit tested code also means the code can be refactored aggressively and with confidence.
Code that is not isolated, is not clean. It is hard to reason about. It is hard to change. It is easy to break (i.e., introduce bugs) when changing. Code that is not isolated has highly intertwined coupling, which is why the code is hard to reason about, hard to change, and easy to break.
The enormous value of unit tests is that they validate the code is isolated.
Note: DRY is great for code, not so good for unit tests. Unit tests should follow the Write Expressive Tests (WET) principle; they should be independent from one another, avoid common setup/teardown, no loops or branches, and follow the arrange-act-assert pattern.
Thank you for the detailed addition!
I agree, unit testing is very good practice
Good article. Very nicely written. I especially dig the conclusion. Some pure honesty in there!
Great read!
Thank you!
Amazing 👌
Thanks, boss!