The idea behind comments seems pretty straightforward: we can add information about code that the code itself doesn’t give. It may be a comment about the purpose of the code. It may be a warning about changing the code in what seems like an obvious way, but would have a negative consequence. Or it may be an explanation of a strange performance optimization.
Whatever the reason, comments are code smells. In essence, each comment we write is a failure. A failure of expressiveness. The fault may be ours, or it may be the fault of the language. No matter the reason, it is still a failure.
Because if your code doesn’t “speak for itself”, and we have to add a comment, then we are dealing with a situation that is less than ideal. This is the real world and we rarely ever achieve the ideal, but nonetheless comments are less than ideal. Often times it is the fault of the technology we’re working with. We are doing something complex and we have to explain why we did this. It’s necessary. But it’s still less than ideal. If the code speaks for itself, then comments are unnecessary.
We can’t control our technology. But we can control the code we write. We can improve our own expressiveness.
Let’s consider the drawbacks of comments.
First, comments are inexact. Code executes EXACTLY how it is written. We may or may not understand the code and how it will execute, but it always is exact. It always runs as written. Comments may be inaccurate. They may say something only mostly true about the code, based on our understanding.
Second, comments are separate from the code they annotate. Therefore the code can change, and the comments may not be kept up. We may also accidentally separate our code spatially from the comments that apply to it, and create confusion by the geographical disparity. But more likely they just get out of date.
Combine the first and second points, and we can get comments that are outright lies, misleading us into doing and thinking things that cost us time and energy at best, and at worst, they may cause us to introduce new bugs into production.
We might think to ourselves: well, we have a team of good developers, we’ll just rely on them to keep comments up to date and keep them with the code they apply to. Sure. And while we’re at it we’ll count on them to unit test 100% of the time and we’ll ask them to stop writing any more bugs. Meanwhile back in the real world, mistakes get made. People get lazy. And why have a situation where this can even happen? Why have a situation that is less than ideal. Expressive code requires no comments. So always prefer expressive code over comments.
Let’s look at one of the most common examples: regular expressions. Regular expressions are powerful. And usually they are extremely succinct compared to alternative methods using more typical code structures. But there’s an old saying: When you have a problem, and you use regular expressions, then you now have two problems. Regular expressions are notorious for being difficult to read and maintain. As a result, we very often put comments on our regular expressions to explain what they do.
This is a failure of the expressiveness of our code. Regular expressions may be succinct and effective, but they are NOT readable. As a result of the failure of the language to be readable, or expressive, we usually have to employ comments. This situation is mostly the fault of the language, but there are ways we can mitigate the effect of something like this.
Here’s an example with the obligatory comment:
But what if we refactored the code to this?
Now the need for a comment has disappeared. This may not be possible with all situations with regular expressions, but it’s certainly a step in the right direction.
Let’s consider an even more common scenario. This is some production code from an Angular project I worked on.
Here’s the original code we wrote: (don’t bother really trying to grok the code, just look at the comments)
And now refactored to not need comments:
By doing this we have added expressiveness, increased readability, and removed the need for comments. And now we can never get in the situation where we have old, inaccurate comments causing us problems.
Every time I write a comment (and I write plenty of them) I always ask myself: “Is there a way to write more expressive code and remove the need for this code?”
So in all cases, prefer fewer comments, and only use comments when expressiveness fails you, preferably due to the limitations of the language or framework and not your own coding.