I have previously discussed the readability of code:
The author must decide who will read the code, and how to convey the important information to those readers. The reader must analyse the code in terms of how it satisfies this goal of conveyance, not whether they enjoyed the indentation strategy or dislike dots on principle.
Source code is not software written in a human-readable notation. It’s an essay, written in executable notation.
Now how does that relate to comments? Comments are a feature of programming languages that allow all other text-based languages—executable or otherwise—to be injected into the program. The comment feature has no effect on the computer’s interpretation of the software, but wildly varying effects on the reader’s interpretation. From APPropriate Behaviour:
[There are] problems with using source code as your only source of information about the software. It does indeed tell you exactly what the product does. Given a bit of time studying, you can discover how it does it, too. But will the programming language instructions tell you why the software does what it does? Is that weird
if statement there to fix a bug reported by a customer? Maybe it’s there to workaround a problem in the APIs? Maybe the original developer just couldn’t work out a different way to solve the problem.
So good documentation should tell you why the code does what it does, and also let you quickly discover how.
We need to combine these two quotes. Yes, the documentation—comments included—needs to express the why and the how, but different readers will have different needs and will not necessarily want these questions answered at the same level.
Take the usual canonical example of a bad comment, also given in APPropriate Behaviour and used for a very similar discussion:
//add one to i
To practiced developers, this comment is just noise. It says the same thing as the line below it.
The fact is that to novice developers too it says the same thing as the line below it, but they have not yet learned to read the notation fluently. This means that they cannot necessarily readily tell that they say the same thing: therefore the comment adds value.
Where someone familiar with the (programming) language might say that the comment only reiterates what the software does, and therefore adds no value, a neophyte might look at the function name to decide what it does and look to comments like this to help them comprehend how it does it.
Outside of very limited contexts, I would avoid comments like that though. I usually assume that a reader will be about as comfortable with the (computer) language used as I am, and either knows the API functions or (like me) knows where to find documentation on them. I use comments sparingly, to discuss trade-offs being made, information relied on that isn’t evident in the code itself or discussions of why what’s being done is there, if it might seem odd without explanation.
Have I ever written a good comment?
As examples, here are some real comments I’ve written on real code, with all the context removed and with reviews added. Of course, as with the rest of the universe “good” and “bad” are subjective, and really represent conformance with the ideas of comment quality described above and in linked articles.
/*note - answer1.score < answer2.score, but answer1 is accepted so should
*still be first in the list of answers.
This is bad. You could work this one out with a limited knowledge of the domain, or from the unit tests. This comment adds nothing.
/* NASTY HACK ALERT
* The UIWebView loads its contents asynchronously. If it's still doing
* that when the test comes to evaluate its content, the content will seem
* empty and the test will fail. Any solution to this comes down to "hold
* the test back for a bit", which I've done explicitly here.
This is good. I’ve explained that the code has a surprising shape, but for a reason I understand, and I’ve provided a reference that goes into more detail.
//Knuth Section 6.2.2 algorithm D.
This is good, if a bit too brief. I’ve cited the reference description (to me, anyway: obviously Knuth got it from somewhere else) of the algorithm. If you want to know why it does what it does, you can go and read the discussion there. If there’s a bug you can compare my implementation with Knuth’s. Of course Knuth wrote more than one book, so I probably should have specified “The Art of Computer Programming” in this comment.
* The command bus accepts commands from the application and schedules work
* to fulfil those commands.
This is not what I mean by a comment. It’s API documentation, it happens to be implemented as a comment, but it fills a very particular and better-understood role.
What do other people’s comments look like?
Here are some similarly-annotated comments, from a project I happen to have open (GNUstep-base).
* If we need space allocated to store a return value,
* make room for it at the end of the callframe so we
* only need to do a single malloc.
Explains why the programmer wrote it this way, which is a good thing.
/* The addition of a constant '8' is a fudge applied simply because
* some return values write beynd the end of the memory if the buffer
* is sized exactly ... don't know why.
This comment is good in that explains what is otherwise a very weird-looking bit of code. It would be better if the author had found the ultimate cause and documented that, though.
/* This class stores objects inline in data beyond the end of the instance.
* However, when GC is enabled the object data is typed, and all data after
* the end of the class is ignored by the garbage collector (which would
* mean that objects in the array could be collected).
* We therefore do not provide the class when GC is being used.
This is a good comment, too. There’s a reason the implementation can’t be used in particular scenarios, here’s why a different one is selected instead.
* Make sure the array is 'sane' so that it can be deallocated
* safely by an autorelease pool if the '[anObject retain]' causes
* an exception.
This is a bad comment, in my opinion. Let’s leave aside for the moment the important issue of our industry’s relationship with mental illness. What exactly does it mean for an array to be ‘sane’? I can’t tell from this comment. I could look at the code, and find out what is done near this comment. However, I could not decide what there contributes to this particular version of ‘sanity’: particularly, what if anything could I remove before it was no longer ‘sane’? Why is it that this particular version of ‘sanity’ is required?
What do other people say about comments?
For many people, the go-to (pun intended) guide on coding practice is, or was, Code Complete, 2nd Edition. As with this blog and APPropriate Behaviour, McConnell promotes the view that comments are part of documentation and that documentation is part of programming as a social activity. From the introduction to Chapter 32, Self-Documenting Code:
Like layout, good documentation is a sign of the professional pride a programmer puts into a program.
He talks, as do some of the authors in 97 Things Every Programmer Should Know, about documenting the design decisions, both at overview and detailed level. That is a specific way to address the “why” question, because while the code shows you what it does it doesn’t express the infinitude of things that it does not do. Why does it not do any of them? Good question, someone should answer it.
Section 32.3 is, in a loose way, a Socratic debate on the value of comments. In a sidebar to this is a quote attributed to “B. A. Sheil”, from an entry in the bibliography, The Psychological Study of Programming. This is the source that most directly connects the view on comments I’ve been expressing above and in earlier articles to the wider discourse. The abstract demonstrates that we’re in for an interesting read:
Most innovations in programming languages and methodology are motivated by a belief that they will improve the performance of the programmers who use them. Although such claims are usually advanced informally, there is a growing body of research which attempts to verify them by controlled observation of programmers’ behavior. Surprisingly, these studies have found few clear effects of changes in either programming notation or practice. Less surprisingly, the computing community has paid relatively little attention to these results. This paper reviews the psychological research on programming and argues that its ineffectiveness is the result of both unsophisticated experimental technique and a shallow view of the nature of programming skill.
Here is not only the quote selected by McConnell but the rest of its paragraph, which supplies some necessary context. The emphasis is Sheil’s.
Although the evidence for the utility of comments is equivocal, it is unclear what other pattern of results could have been expected. Clearly, at some level comments have to be useful. To believe otherwise would be to believe that the comprehensibility of a program is independent of how much information the reader might already have about it. However, it is equally clear that a comment is only useful if it tells the reader something she either does not already know or cannot infer immediately from the code. Exactly which propositions about a program should be included in the commentary is therefore a matter of matching the comments to the needs of the expected readers. This makes widely applicable results as to the desirable amount and type of commenting so highly unlikely that behavioral experimentation is of questionable value.
So it turns out that at about the time I was being conceived, so was the opinion on comments (and documentation and code readability in general) to which I ascribe: that you should write for your audience, and your audience probably needs to know more than just what the software is up to.
That Sheil reference also contains a cautionary tale about the “value” of comments:
Weissman found that appropriate comments caused hand simulation to proceed significantly faster, but with significantly more errors.
That’s a reference to Laurence Weissman’s 1974 PhD Thesis.