Comments Mean Failure

I don’t like comments, because having to comment a piece of code means the code itself does not express everything there is to know. 

The purpose of comments

Comments are there to express something that is not immediately apparent from the code. They are meant to clarify, where clarification is needed.

Clarification in this case means either providing additional information that can not be found in the source code or making information more prominent that would otherwise be difficult to extract from the code for whatever reason.

Ways in which comments can express failure

There are different ways in which comments can express failure, most of them being programmer failures. There are times where a comment simply does not accomplish its purpose, and sometimes there are better ways than using a comment.

Failure to add information

Comment redundancy

In contrast to some very old style guides, it is not good to comment each and every line of code. Consider this piece of code:

int i = a + b; //adds a and b

Really? Is that an addition? Of course it is. The comment is not needed for us to see that. The comment does not help, it only adds noise, making the code less maintainable.

I say less maintainable, because the comment that does not add any useful information will be read by everyone who reads the code. Because we expect it to add information. Since that is what comments are for. This comment is utter noise and only costs the reader time.

This is a textbook example of a bad comment and you probably have seen it before. You probably think that nowadays nobody will write redundant comments, right?

Well, here’s a thing. In a code base I currently work on there are a ton of redundant comments. Look at this exemplary header:

#ifndef Bazinga_H
#define Bazinga_H
//------------------------------------------------
  Some Copyright notice...
//------------------------------------------------

// includes
#include <vector>
#include <string>
#include "someheader.h"

//namespace
namespace MyNS
{

//forward declarations
//typedefs
class FooBar;

//class
class Bazinga
{
public:
  //constructors
  Bazinga(int);

  //destructor
  ~Bazinga();

  //operations
  void meow(FooBar const& fb);
};

} //end namespace
#endif //defined Bazinga_H

All those comments are redundant. And they make the header a mess to read. Consider the header without the comments:

#ifndef Bazinga_H
#define Bazinga_H

#include <vector>
#include <string>
#include "someheader.h"

namespace MyNS
{
  class FooBar;

  class Bazinga
  {
  public:
    Bazinga(int);
    ~Bazinga();

    void meow(FooBar const& fb); 
  };
 
} 
#endif

Is that less clear? I don’t think so. One comment that could have been left in is the “end namespace” comment, if the namespaces are deeply nested and you don’t want to indent the class definition accordingly.

Note that some kind of copyright comment can be company policy that you can’t easily get rid of. On the other hand, if someone gives away his source code at all, it is often accompanied with a license.txt or something similar.

So this could be labeled “failure to keep politics and legal stuff out of the code”. After all, the code is meant to be compiled and executed, and having a legal note in it to some extend violates the SRP 😉

Avoid redundant comments as they are not more than a maintenance burden.

Lies

There is another prominent example in the header for reasons why comments can be counterproductive. Did you spot it? It’s the line that says `//typedefs`. There is no typedef.

That is the main problem with comments: they can lie. When people edit code, they tend to forget editing the comments around it. What remains is a lie that will confuse and even mislead the later reader.

We can’t guard against that fact. Comments can’t be unit tested, and since they usually are written in plain English (or whatever language your team uses) they can not even compile them.

Avoid comments that describe what the code does, because they can (and often will) turn into lies.

Failure to write clear code

The need to describe what a piece of code does in a comment often shows that the programmer who has written the comment does not think the code to be clearly written.

In most cases this is pure laziness. We have lots of tools at our hand to clarify such things in code instead of writing a comment about it. Those tools include, among other things, descriptive names and layers of abstraction.

If you give functions and variables proper names, their purpose does not need to be commented. If you take a block of code and factor it out into a properly named function, you won’t need to comment what the block does, because the function name will tell us.

Prefer refactoring and stating your intent in the code itself to commenting.

Failure to write self documenting code

There is a whole category of comments that can be tested for validity. What I mean are the comments often found in headers, that tell us about what a function does, what arguments it accepts and what preconditions have to be met and so on.

How they can be tested? Transform them into unit tests. Write a test that shows how the function fails if you pass a null pointer. If you document your classes and functions by providing unit tests, any comments that describe some behavior will be redundant.

Prefer documenting you classes with unit tests rather than with comments.

What about comments used by tools for documentation extraction, like doxygen? Make sure you export the implementation of your unit tests. They are documentation after all, and you won’t need redundant doxygen comments.

However, if you use comments that are only meant for the external documentation, go ahead and use them. They are only comments from the C++ perspective, but are meaningful code for the extraction tool.

For example, do links to the unit tests that document your classes.

Language failure?

It is not always possible to express what you want to say in a clean, clear and readable way that also compiles. In that case, there is little to do but adding a clarifying comment.

In such a case the comment indeed does not hint at a programmer’s failure but at a shortcoming of the language. However, some things are outside the scope of even a general purpose language, so I would not say that such things are automatically “language failures”.

After all, that is the reason why comments are part of the langue.

As a last resort, better use a comment than having an unclear piece of code.

Facebooktwittergoogle_plusredditlinkedinFacebooktwittergoogle_plusredditlinkedinby feather

12 Comments

  1. SeattleC++

    Code expresses implementation. It is too low-level to express design or intent. Comments can express things the code cannot. They are therefore a Good Thing.

    Comments can lie. So can code. If we don’t write comments because they can be used poorly, where do we stop? if we remove every feature from C++ that is capable of misuse, the resulting language (to the extent it has anything in it at all) would be far less useful. It is a poor workman who blames their tool.

    Reply
  2. Andrzej

    Very controversial entry 😉 It’s a bunch of very interesting points, but it feels like you focus only on `poor comments` and try to explain why they are bad. But it’s not enough to justify skipping comments, in particular quality comments. I am a strong supporter of `comments`, but it is not easy to write good and useful ones. Probably as challenging as writing quality code. A lot of programmers struggle to express their reasoning using plain English and hence write useless `comments`. I feel that this entry could be a bit more constructive.

    I agree with you that code should comment itself. However, code comments ITSELF as opposed to what the programmer intended to write. If there’s a bug in the code (and there are always bugs in the code) then the source code is there to tell me what the code does. It doesn’t tell me what the original author intended to write and what he thought the code was doing. Well written comment could save hours of guessing.

    Reply
    1. Arne Mertz

      Hi Andrzej, I know this is quite controversial, and not only since some discussions about the post 😉

      I don’t want to focus only on poor comments, but also on comments that are felt necessary due to poorly written code. Like I have written in the last section, I acknowledge that there are situations where the code can’t express anything that has to be said and comments are appropriate. However, that is much less often the case as many people pretend it to be. The ability to write a sloppy comment often serves as excuse to not think enough about better expressiveness of one’s code.

      That often is the problem in your example: If the programmer’s intention is not simply obvious from the code, then the first reaction should be to try making the code more obvious, which also makes bugs more obvious. The clarifying comment about one’s intentions should again be the last resort, but is still better than complicated and uncommented code.

      Reply



  3. Sebastien Kramm

    I strongly disagree with your point of view, and I feel most of your post gives an answer to an ill-posed problem.

    Of course, it makes no sense to comment “constructor” just above the constructor.
    Of course, comments can lie (or be erroneous), most of the time because they are not updated when code changes. But that’s not an excuse for not commenting.

    quote: “Prefer documenting your classes with unit tests rather than with comments.”
    No.
    1 – Unit test are probably way over in another file, reaaaally far away. Unless you suggest adding those to the current code, to make it even more cluttered ? 😉
    2 – Having to understand an algorithm by checking the unit tests makes no sense. The purpose of unit tests is to make sure each function does what its supposed to do, not to describe how that task is implemented.

    *I* have been through the process of handling someone elses code (lets say between 10k and 50k loc), with hardly any comments (and maybe not very good code, and of course no specs as it’s research code), and, believe me, that code base suddenly becomes useless.

    I do mostly scientific/numerical code, and when I have a pretty complicated algorithm to implement, then I (try to) add a lot of comments to explain to the reader (say, me in a few months) how this and that works (aside from U.T.).

    Sure, bad commenting is a bad practice, but **no comments** is also very bad.
    On the bottom line, why I disagree with your post is that it can encourage junior devs into some “no comments” habit: “c’mon, that code is self-explainatory”. Sure it is…. for yourself and because you wrote it yesterday ! Come back here in a couple of months, and explain to me how that ‘foo()’ function solve that problem, we’ll see!

    Moreover, modern C++ is great but the counterpart of the conciseness it offers is that for the less experienced reader, understanding what a single line does can be very difficult. A few words can easily save hours.

    Anyway, keep on writing.

    Reply
    1. Arne Mertz

      Hi Sebastien, thanks for your opinion on this one!
      For you coming from the scientific/numeric part of C++ land the case is a bit different than e.g. for a garden variety business application. I completely agree that scientific algorithm implementations have to be well documented by comments.

      I think this is mainly due to two reasons: Those algorithms tend to be much more terse and complex than the comparatively easy stuff going on in business applications. In addition they are not easily described by prose function and variable names that would make the code clear and readable.

      However, I think that is a somewhat special situation and most codebases have less need of comments then what you describe. I have seen different business application code bases with a few million line of code. If it was not for misleading, convoluted and cryptic coding and naming style, those code bases would not need much more than one comment each few thousand lines of code.

      What I want to say is that 10-50kloc can work without comments if the code is written clear enough.

      As for the unit tests, I stick to what I have written. With a proper IDE and naming convention for the tests, the unit test is not so “far away” from the class it tests. In fact, it should not be more than a few clicks or keyboard shortcuts. And no, I do not recommend putting the test code in the same source as the code under test.

      Of course a unit test can not explain the implementation of a class, nor should it. It should not even know about that implementation. What I meant to say is that unit tests are best used to document the use of a class and its interface.

      As a last point, I don’t want to encourage anyone to mindlessly commit to a “no comment” habit. I want to make them aware of the fact that comments can be a hint to a flawed code and to thik twice if whatever the comment means to clarify can not be clarified in the code itself.

      Reply
  4. Björn Fahller

    I believe there is one exception where comments are useful, and that is when they say why (or why not.)

    Good names tell what the code does. Good structure makes it obvious how the code does it. But neither names nor structure can explain why the code is done the way it is. Memory constraint, performance, standard compliance may be be explained with comments.

    Reply
    1. Arne Mertz

      Hi Björn, thanks for that comment 😉
      I agree that the examples you mention are exceptions where comments can be appropiate. Sadly in my experience these are the kind of comments people only rarely provide.

      Reply
      1. Björn Fahller

        Unfortunately I share your experience, Arne.

        I think the exception rule can be distilled to “comment what the code cannot say.” Emphasis on “cannot.” If the code can say it, let it do so. If the code can’t, and it’s important, a comment is better than to leave it to guesswork. What the code can say depends on the language and the situation. Pre/Post conditions are part of some languages, so no need to comment them in those languages. In modern C++ unique_ptr, shared_ptr and [lr]value references makes some intent obvious, so no need to comment them. In C or older C++, ownership pre/post call is sometimes non-obvious, and cannot easily be expressed by the code itself, so comment it.

        Reply

Leave a Reply

Your email address will not be published. Required fields are marked *