We spend little or no time teaching programmers how to write good comments.
This is surprising, when you consider how often “total lack of comments” or “poor comments” are cited as evidence that certain modules (or the programmer who wrote them) are the worst thing that ever happened to the technoverse.
I happen to think that there are much yuckier tech things than poor or missing comments in code. But I still think our general level of comment proficiency is lower than it should be.
Here is my attempt to raise the bar a little.
Why We Comment
Sooner or later, most interesting programming problems require a sophisticated mental model of a problem. Building these models is hard work, and once we have them, we are paid to share with our team (or our future selves).
The best way to share mental models with other engineers is to code with names and syntax that make our meaning crystal clear. But formal expressions often lack the semantic richness, the subtlety, and the scope that we need for full knowledge transfer. So we comment to make sure the sharing succeeds.
It’s critical that programmers get this.
Shared mental models make complex software possible. Shared mental models, not lines of code, not patents, are the most valuable embodiment of a company’s IP and technical assets. (More about this in another post.)
A programmer that leaves critical knowledge unshared is seriously derelict in her or his duty, no matter how well the code works. I’ve quoted Martin Fowler on this point before, but I’ll do it again, because he’s so, so right:
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
Some of the more hilarious comments I’ve ever read are connected with moments when this principle goes out the window. (Go to the bathroom before you read this collection, so you don’t wet your pants laughing.)
How to Write “Good” Comments
Seriously, make a habit of asking yourself: “What must I explain, that I can’t easily say in code, that instills a healthy mental model of this class/module/function/app?” You can’t go far wrong it that’s your point of departure.
You’ll find yourself naturally aligning with guidelines like this:
Good comments supplement, never replace, what can be said with code.
Instead of commenting that something is an “IN” parameter, make it const. Instead of commenting that a parameter should never be null, make it a reference or add a precondition. Instead of commenting the semantics of a variable, give it a name that make its semantics clear. Tools will then enforce what you say, and guarantee that it remains accurate.
When you have conveyed as much as you can with pure code–and only then–any important aspects of your mental model that don’t come across go into comments.
Good comments explain the subtle and important, not the obvious or irrelevant.
The comments of great coders tend to focus on why a particular tradeoff was chosen, what ramifications might surprise the next guy, or where a hidden dependency lurks. Those who follow gain expertise with less battle scars–or else they diagnose old mistakes with greater confidence.
Because good comments avoid trivia, they tend to remain accurate. Others appreciate their value and buy in to their upkeep. This becomes a way to reinforce effective communication and best practices for an entire team.
When comments become inaccurate, it is usually an indictment of their relevance, not of sloppy maintainers. Consider the mandated comments in “How to turn coding standards into epic fails — or not.”
Good comments are concise.
Conscious of their supporting, not starring role, good comments read quickly and get out of the way. (Compare Yegge’s silly counter-example in “Portrait of a N00b“.) Rather than recapitulating an entire RFC, good comments use a hyperlink. They use formats friendly to the IDEs of the team, so you can collapse them as needed. They avoid repetition as much as possible.
Comments shouldn’t be used to disable code blocks.
Disabling code is worse than just a departure from the purpose of comments–it’s actually a contradiction. When you disable a block of code, you tend to make it harder to understand, because the natural reaction of the next coder will be to wonder why the code is still there at all. And you have to write a (… wait for it …) comment to explain yourself.
Commented-out blocks rarely get maintained or re-enabled. They clutter and obscure.
We have VCS technology, folks. Unless the disabling is very temporary, if you don’t want the code to run, delete it. Or if you must retain it for post-mortem purposes, use an #ifdef. Or put it in an inert file and point people there.
What About Javadoc, Doxygen, $Id, and // TODO?
To make automated tools happy, we may be pressured to compromise a bit on the principles I’ve just offered. Some tools that process code comments complain unless you document every parameter, every return value, every possible exception. IDEs support snippet insertion to facilitate this, which leads to lots of boilerplate comments that have low value. The snippets make warnings go away, which encourages developers to be lazy.
I don’t believe in generating documentation outside the code for internal developers, as a general rule. Developers are code-centric. I have never met a single developer that read external documentation on their own codebase (except overviews when they’re first learning), but I have met many who think hard about comments left by co-workers. My conclusion is that comments intended only for an internal audience should be prejudiced toward consumption in your team’s code editors, not prejudiced toward the needs of external views.
I would accept compromises to comment quality IFF there’s an important audience for your functions outside your own internal dev team (e.g., you’re writing an SDK), and your tools cannot be made happy with human-driven choices about comments.
Make thoughtful improvements to one function where you know it’s hard for others to build a complete and accurate mental model.