A little history
Over the years, my view on commenting source code has changed substantially. As an immature developer, I believed that it was worthwhile to comment the code in such a way as you could tell what the code was doing simply by reading the comments. This was my philosophy, and I followed in judiciously.
Why I changed (grew up?!)
There are two major problems with this.
Changes to the source require changes to the comments.
But this, despite what some would say about ‘don’t be lazy and just update the comments’, is much more difficult than simply ‘updating them’.
The real issue is that comments are written in a human context-dependent language.
Think about this for a minute. Here some rudimentary linguistics is quite helpful (and not of the nonsensical Derrida variety).
So what is the issue? The problem here is that meaning is found at the discourse level in human languages. Paragraphs are the real substantial unit of meaning. This means that changes to such commenting are vastly more involved than even changes to their source code. It is like rewriting a paper after changing the thesis, or major point. It isn’t easy to make it coherent.
2. Code can be expressive.
This is nothing new, of course (cf. Bob Martin’s Clean Code). Really, I have become increasingly convinced that anything that can be expressed in code should be. And that really covers almost all of it. We have human-readable languages for just this reason.
One caution come to mind, however: Brevity. Be as brief as reasonably possible. That is, don’t write excessively long camel-case names. Not because it is wrong per se, but because they become hard to read, thus negating their purpose. Use the context of the code. Interestingly, this runs into the same tension as point (1) above, since you begin to have “discourse level” interdependencies in the code.
The problem is that you cannot include all the surrounding levels of abstraction in the function name. If you do, it becomes too cumbersome to be realistically maintainable. However, this runs into the tension of context-dependent function names.
The key here is to make sure that every name actually exists at one level of abstraction, in my view, and to make sure that you’re applying dependency inversion judiciously. Then you can have sufficiently long names that don’t get either too cluttered, or so obscure as to be useless.
Work to become more expressive with English. Learn how to compress thoughts. Here’s a way to practice this. Write a five page essay on some topic, and then try to say the same thing in three pages.
With that said, where do I think we need comments?
The first thing that comes to mind is APIs. But I want to suggest that this is a red herring. APIs need commenting for a particular reason, in my view: Leaky abstractions.
’m beginning to wonder if this category couldn’t encompass all necessary commenting. The very nature of a leaky abstraction is, bearing with my tautological explanation, that an underlying, concealed, abstracted implementation can or will affect the behavior of the abstraction in a way that is not obvious at that level of abstraction.
So if I have two phones, and one of them is a cell-phone, and the other a sat-phone, the former will work in tunnels (most of the time), but the latter will certainly not. The underlying implementations leak through and affect the usage and behavior of a potentially common user interface.
You need to understand something about the implementation to know whether you can have a crucial call when you’re about to go underground for a while.
This is where comments are truly useful.
Of course, if we take any class or API, it may not be obvious how one should go about using the functionality; e.g. What do I call first?
However, I think that well-written tests are far better than commenting here. Why not ship your acceptance tests with your product? They should be human-readable, and they aren’t just an instruction manual. They are “examples” and “instructions” that actually self-verify. This can’t but help everyone involved.