“Every comment represents a failure to make the code self explanatory.”
&em; Robert C. Martin (Uncle Bob)
One of the better-known sentiments from Uncle Bob’s Clean Code is that
comments are bad*. More precisely, the idea is that comments are a last resort
option to try and mitigate “bad code”.
Sometimes this seems to lead to a logical fallacy that goes like this: “Comments
are a sign of bad code, and my code can’t be bad, so I won’t write comments”.
This can progress to an even worse line of thinking: “Comments are for bad code,
therefore if I don’t write comments, then my code can’t be bad”.
*Uncle Bob himself does not claim that comments are always a bad thing, as you
can see in the linked article above and in this
summary.
However, I would go further and suggest that code comments are generally
positive and rarely a bad thing. In any case, the “comments are bad” meme seems
to have taken hold in a lot of places.
Mental states are ephemeral
“Debugging is twice as hard as writing a program in the first place. So if
you’re as clever as you can be when you write it, how will you ever debug
it?”
&em; Brian Kernighan, The Elements of Programming Style
Figuring out how to solve a problem and implementing that solution can be
straightforward or somewhat challenging, but in either case, your mental state
while doing so holds a lot of contextual information about the problem and
solution.
The clichéd metaphor for what happens next is that it’s like sand falling
through your fingers. Even a day later you’ll find yourself lacking some of that
context if you come back to the same code, especially if you’ve been working on
something else in the interim.
How much worse will it be for someone else to try and build up the same mental
context, or yourself in six months’ time?
Comments are a chance to try and preserve some of this context at exactly the
point where it is relevant. The structure and naming of your implementation code
is also important, but it cannot replace the assistance of comments written in a
language “designed” for conveying information between human minds.
Your code is not self-documenting (even to yourself)
The term “self-documenting code” has become a bit of an in-joke among software
developers, and rightly so. Stereotypically, it’s associated with oblivious
team-members who inflict their idiosyncratic and incomprehensible code on others
while proclaiming that it is self-documenting.
There’s an implication with “self-documenting code” that if you don’t
understand it, it’s your fault and not the fault of the code.
If that’s true, then what is the point of using modern, higher-level languages
other than to make code manageable and readable for humans? The machine
understands machine code and does not care about how understandable the source
code was. We could just write machine code and blame it on the human reader if
they find it hard to follow.
Imagine a textbook full of “self-documenting diagrams”, furniture with
“self-documenting assembly”, or a book of “self-documenting recipes”. These
would be nice if they worked, just as self-documenting code would be. They don’t
work because in each case we’re trying to transfer knowledge about how something
is intended to function, and that is improved with direct explanation.
On that basis, we should use every tool we have to try and make the source code
easy to understand. Comments are part of that toolbox.
In case it needs pointing out, I’m definitely not suggesting that huge methods
and god classes with a lot of comments are the way to go.
Someone always points out that instead of writing a comment saying “Handle issue
where the DodgyAnalytics.io API sometimes sends us malformed data”, you should
break out a method explaining that in the name.
Absolutely break out a method called handleDodgyAnalyticsMalformedData() .
Alternatively, break out a class called DodgyAnalyticsMalformedDataHandler , or
have a DodgyAnalyticsResponse interface and implement it with a
MalformedDodgyAnalyticsResponseDecorator that wraps the problem up so other
code doesn’t have to know about it.
Those are all definitely options you should consider, and are probably better
than just fixing the malformed data inline and chucking a comment on it. But if
you’ve broken out well-named methods, classes or interfaces, put comments on
them explaining what they’re for:
/**
* This decorator detects and fixes the issue where the DodgyAnalytics.io API
* sends us malformed data due to blah blah blah.
*
* This issue has been raised with them here: issuetracker.dodgyanalytics.io/123
*
* The responses are supposed look like this: docs.dodgyanalytics.io/foobar
*
* See relevant tests here: @SomeNameSpace.SomeTestClass
*/
The comment does not mean the code is bad. It will be helpful to anyone else
investigating a bug in this area of the codebase in six months’ time. It makes
the code more grep-able. It helps people figure out the context and get
productive more quickly.
Pragmatism is a good quality in software development. If you ask software
developers what their job is, a bad answer is “my job is to write code”. As the
maxim goes, our job is not to write code. Our job is to solve problems and
improve things for ourselves and others.
Fixating on the code itself can lead to this kind of anti-pragmatic thinking. If
the code is all that exists, then we’d better make it pristine. There’s
something almost arrogant and pretentious about believing the code you’re
writing can be a testament to cleanliness and correctness. High opinions of the
code are probably not objective, and even if they were, getting it from 95% to
99% quality (if that can be measured) will consume vast amounts of effort that
we should consider spending elsewhere.
We’re not writing a novel for publication, we’re trying to make something
useful. Code quality is a necessary part of that, but it is not the whole.
Hopefully you write tests for your code and believe that automated testing is
important. One of the purposes of tests is to function as a kind of
documentation of the test subject. Looking at tests is often helpful for
figuring out how things are supposed to work. Aren’t comments important in the
same way?
Similarly, another crucial idea with automated testing is the recognition that
we are fallible and likely to make mistakes in our implementation. Tests add a
layer of confirmation that the implementation is correct. If you can be fallible
in the implementation of functionality, can’t you also be fallible in making
code as clear and readable as possible? Comments can add a layer of contextual
understanding to remedy this.
Finally, automated tests serve to increase confidence that some functionality
still works correctly even after the original development context has been
forgotten. Tests will identify if something is later broken without you needing
to remember why or how it used to work. Comments can be similar to this – the
original knowledge might be forgotten, but they can help to preserve it and
point you in the right direction when you come back to the code.
One common objection to comments (and even documentation in general) is that
they get out of date, and are then worse than no comments at all. I would say
that’s a false line of reasoning.
Should we not write any code at all because the code will get out of date with
the requirements? (Note that not writing any code can be a valid solution that
is often overlooked.)
Even an out of date comment adds more information than having no comments at
all. “Wrong” information is still information that can be used for figuring
things out.
If you’re trying to make a chocolate cake, having a recipe for a coffee cake
would be better than nothing; you can at least take some hints from it.
Similarly, it’s useful to know that a piece of code at least used to solve the
issue where bad data from the third party API broke this workflow, even though
we’re now solving that issue earlier in the pipeline. You don’t have to take the
comments as gospel; they can be nudges in the right direction or insights into
what was going on at the time the code was written.
Leave references
Comments don’t have to just be plain text. Including links is often beneficial,
for example:
- Relevant Stackoverflow and Github issues,
- Links to more detailed docs,
- Links to tools such as a Regex helper for a given regex,
And so on. I would say, if in doubt, comment it. It might help you or someone
else later, and it preserves useful information in context.
“Indeed, the ratio of time spent reading versus writing is well over 10 to 1.
We are constantly reading old code as part of the effort to write new code.”
&em; Robert C. Martin (Uncle Bob)
Another Uncle Bob quote, usually paraphrased as “code is read more than it is
written”.
Because we spend so much time reading code, make sure that the hard-earned
knowledge from that doesn’t disappear into the ether. Put your learnings into a
comment and preserve them. The code is a living document, and these additions
keep it healthier for everyone working on it.
To reiterate, comments are by no means the sole way of doing this, but they are
a helpful addition. Another useful approach is exploratory
testing, again showing some
of the links between commenting and testing.
See also
View post:
Please comment your code
|