Lately I’ve seen and heard different people preaching code without comments. They say that if you write good, clean code that explains itself, there’s no need for comments. They go on to say that comments are useless once you use the right names for variables, functions, classes. They also say that it makes you write smaller functions, and split classes into smaller classes because their names have to say everything about them; and you can’t say much in a 20 character function name. Also they say that once you update the code, you may forget to update the comments. So it’s better to not write them at all.
Of course, this idea was adopted very rapidly by many people. We don’t have to write comments anymore! Yes! Everybody that never commented any code in their lives, suddenly has a reason for continuing to do so. If someone had to write both code and comments, and now only has to write code, it’s easier, right? It takes less time since you don’t have to write something extra. Another reason for the rapid adoption is that most programmers have good maths skills, but not so good English writing skills. No wonder that many people have jumped aboard of that train, and started spreading the idea that comments are useless.
So, I’ve been reading about this, seen it in a couple of books, seen it a some articles, blogs, and have been listening to it from my coworkers. I’ve always avoided to argue on this one since I wasn’t directly affected.
But now it’s time to defend the comments. Somewhere along the road they’ve been reduced to a redundant, maybe outdated explanation of the code, an excuse for bad code that cannot explain itself. I’ll show you that they’re more than a redundant explanation of the code. Much more than that.
Now it’s time for me to stand up and preach a new idea – code with comments!
How things usually are
You see, we programmers usually write crappy code. Sometimes we make all the mistakes that the coding books tell us not to: long functions, functions that do many things instead of just one, variables called myVar1, hard-coded values, creating interfaces with useless methods, sometimes useless parameters. Sometimes our code is written as if we’re trying to win Obfuscated C Contest, in whatever language we’re coding.
We do all these things, no doubt about it. The idea is to see them somehow and fix them. How do we do this? How do we improve our code? Usually we run the code, and nothing works and there are a lot of bugs. We fix those bugs and change the code a bit. Then another person wants to use our code, but they discover hard-coded things, so we change the code a bit, making it more generic. And so on. After six months a simple function, as simple as printing ‘Hello world’, starts to look usable.
There has got to be another way to improve the code – and faster!
Improving existing code
Another way to improve code is to review it. We could review the code and change function names, split long functions, move generic functions out of specific classes, change interfaces, etc.
The thing is, most programmers do not ‘review’ the code. They write it, and if it works – perfect! They’re not going to re-read the code and make changes to variable names. That’s just time wasting.
So, how can you force a programmer to review the code? Well, in my case the answer is very clear – comment the code.
Once you start commenting the code you’ve written, something magical happens: you are forced to review the code, and write down what it actually does. Let’s meet Joe. Joe is a young smart programmer – and of course lazy. Joe has created a function that should save the details of a new user to the database, and which also sends an email to the admin. This is how his function would be called:
OtherModule → call saveUserDetails(arg)
Now, when Joe starts commenting that function he’s forced to write that down “this function does this but has also this side-effect of sending emails”. Also, one variable is called ‘arg’ but it should hold the user details, fully complete and valid. Joe would have to write that down too. Joe would have to write how the function behaves in case of errors: what if the database is down? What if the email system is down? Should he ignore the error from the email system? Joe is about to write a very, very long comment. He doesn’t really like writing.
There is another reason holding Joe from writing this comment. Joe has a strong ego and he wants that his peers think that his code is perfect. Should he say that in the comment, his peers would surely see the imperfections.
So, what does Joe do? Normally, he wouldn’t write any comments and let the function as it is. But since he has to write the comment, he starts writing it, mumbling about the injustice being made, having to write English words like in school. So, he starts writing the comment, but then realizes that instead of writing a very lengthy comment describing the side-effect and error handling, he can simply split the function in two and write a very shorter comment. This way he does what he likes: deals with code. He renames ‘arg’ to ‘userDetails’, moves out the code that sends an email to a different function. Now, he has two functions, each doing only one thing, short functions, and with clear parameter names.
This is how his functions would be called:
OtherModule → call saveUserDetails(userDetails)
OtherModule → call sendEmail(“New user”, “email@example.com”)
Now, it’s very easy to write the comments for each function. In the end, everybody wins, as the code is clearer and more robust. And with comments, too!
Writing future code
Sometimes it’s easier to write the prototype of a function, and write the comments about what it should do before writing any code. Some say that they already know what it should do, and they can add the comments later, or not at all. However, sometimes things seem very clear, but once you start to analyze them you discover they’re actually very fuzzy. It’s like a dream – you think you remember it all, how you’ve been attacked by an animal while walking down the street; but when you start telling this dream to another person you can’t remember if you were attacked by a tiger or by a lion, or if you were on the street or actually at the seaside. The same thing happens with code – you think you can design the perfect function names and parameters, but when you write the code you discover lots and lots of missing parts. By writing the comment before, you can fix those parts without having to modify a lot of code.
This argument is similar to writing functional specifications for a product – the function comment, the class comment, even the comment inside a function that describes a smart algorithm – they all define what you’re going to do. It’s a contract between you and future you that writes the code. For example, you specify in the comment how the function should behave if the database is down, how people should use this function, potential limitations (not thread-safe, for example), etc.
Once you start writing the comment, you take the time to think about this function. And when you start thinking about something, that’s when you see other use-cases, that’s when you see simpler ways of accomplishing what you want to do, that’s when you see potential pitfalls, etc.
Then, after the comment is done, you write the code, which will be an easier job now. It’s similar to ‘Test driven design’ – call it ‘Comment driven design’ if you will. (Another name would be ‘Specification driven design’, since the comment actually acts as a specification, but ‘specification’ is a word that’s too generic).
How can comments improve reading code
Everybody in the industry knows it: code written by other people is crap and difficult to use. Why is it so? Because it’s hard to understand. Reading code is hard.
Sure, the name of the function can say something about what it is supposed to do. So do the names of the parameters. So does the name of the class. But they can only say as much, as they’re supposed to contain only few letters or words.
But! Suppose you had comments in English, using real sentences, with a subject, and a finite verb; comments that explain what a function does, how a clever algorithm is supposed to work, or why it does things in a peculiar manner. Now it’s easier to understand, right? Even if the comment is a bit outdated, you can still understand what the programmer was thinking when he wrote the code – what he assumed, what he missed.
Have you noticed how standard libraries are very documented, with comments for each function (even dumb comments, for setters and getters). How would you use use a library that’s missing comments? No way, right? It would be something along the lines of reverse-engineering. Yet some people do this every day, when it comes to using other code from their project, written by other people. Many developers understand the code that they’ve just written and they don’t feel the need to explain what it does. However when some other person wants to use that code, she will have questions such as: is it fine to send null values to this function? What is the third argument supposed to be? Does this function work for positive numbers only? What does it return? How does it handle errors? That’s where comments are really answers to all these questions.
Other benefits of writing comments
Avoiding premature optimization
Everyone has heard of premature optimization. It happens when smart programmers try to be smarter and write fast code. I won’t go into details, but we all know that premature optimization is bad for many reasons – wasted time, complex design, tightly coupled components, etc. Some British fellow called Hoare went as far as to say that premature optimization is the root of all evil.
Now, assuming that a programmer has to write comments, she will be eventually forced to comment her smart algorithm that optimizes searching through the list of someone’s pets. So, she has to write down all the steps of the complex algorithm, explain the hacks made for speedier operation, maybe even explain the limitations of her algorithm (“works only with cats”).
There are two options here:
a) she writes the code that does the premature optimization and the long comment.
In this case, ah well, at least a future person working on that code will understand it.
b) she quits writing the code that does the premature optimization.
This can happen because she realizes that it’s easier to write simple code which requires simple comments, or because she realizes that the limitations of her algorithm make it unusable, or because she realizes what a complex design she’s just created.
Improving writing skills
In time, if somebody keeps commenting code, his writing skills will improve, no doubt. Better writing skills means also better communication skills overall. This is useful because that person will be able to write better specifications, to write better emails requesting a bonus, to convey his ideas better when talking to his manager, or customers, or lover.
When writing a software program, the goal is to write code that’s easy to maintain, reuse, and extend. How do we do that? By making sure the code is easy to read. This implies a lot of tools, such as proper naming, short functions, avoiding magical numbers, etc, but it cannot exclude comments. When it comes to communicating our coding intentions, we need all the tools we can get! Not only code comments are not outdated, but they’re more and more badly needed, as the software we develop is increasingly complex.
What do you think?