Indented comments

When I was starting out as a programmer, I used to work with an
engineer who insisted that we write the comments to our C code at the
end of the lines, starting in column 80. "That way", he said, "I can
read the code without being distracted by the comments". I think it
was more his assembly code experience that made him prefer that style,
but unobtrusive comments are certainly a worthwhile goal. Later, I
came up with my own solution to the problem, and although in over
twenty years I have seen only one other programmer who uses it, I
use it all the time in my projects and I wish more people used it too.

The idea is to harness the power of whitespace and to simply move
comments out of the way by indenting them once. For example, to make
this a bit more readable:


# Lorem ipsum dolor sit amet, consectetur adipisicing elit,
# sed do eiusmod tempor incididunt ut labore.
my $lorem = @ipsum;
# Yes, this is a comment.
$nesquiat[0] = 42;

most people will add a blank line like this:

    # Lorem ipsum dolor sit amet, consectetur adipisicing elit,
    # sed do eiusmod tempor incididunt ut labore.
    my $lorem = @ipsum;

# Yes, this is a comment.
$nesquiat[0] = 42;

But, once your mind isn't put off by the fact that yes, the comments
are indented, you will find that the code teases itself right out of
the text's stream and that this works much better:

        # Lorem ipsum dolor sit amet, consectetur adipisicing elit,
        # sed do eiusmod tempor incididunt ut labore.
    my $lorem = @ipsum;
        # Yes, this is a comment.
    $nesquiat[0] = 42;

Here's another example with a bigger block of code:

    sub new {
        # Determine if we were called via an object-ref or a classname.
        my $this = shift;
        my $class = ref($this) || $this;
        # Any remaining arguments are treated as initial values for the
        # hash that is used to represent this object.
        my $ctpath = clearcase_config('CLEARTOOL');
        # The versions also vary in their formatting of the following.
        my @defaults = (
            qw( -output -chomping ),
            '-abort'    => undef,
            '-execpath' => $ctpath,
        );
        my $execopts = execopts_parse(@defaults, @_);
        my $self = { '-execopts' => $execopts };
        # Bless ourselves into the desired class and perform any
        # initialization.
        bless $self, $class;
        $self->initialize();
        return $self;
    }

It becomes:

    sub new {
            # Determine if we were called via an object-ref or a
            # classname.
        my $this = shift;
        my $class = ref($this) || $this;
            # Any remaining arguments are treated as initial values for
            # the hash that is used to represent this object.
        my $ctpath = clearcase_config('CLEARTOOL');
            # The versions also vary in their formatting of the following.
        my @defaults = (
            qw( -output -chomping ),
            '-abort'    => undef,
            '-execpath' => $ctpath,
        );
        my $execopts = execopts_parse(@defaults, @_);
        my $self = { '-execopts' => $execopts };
            # Bless ourselves into the desired class and perform any
            # initialization.
        bless $self, $class;
        $self->initialize();
        return $self;
    }

Another place where this comment indentation makes files so much easier
to scan is some configuration files. Take this excerpt for example:

    # Don't request or allow negotiation of any options for LCP and IPCP
    # (use default values).
    #-all

# Disable Address/Control compression negotiation (use default, i.e.
# address/control field disabled).
-ac

# Disable asyncmap negotiation (use the default asyncmap, i.e. escape
# all control characters).
#-am

# Don't fork to become a background process (otherwise pppd will do so
# if a serial device is specified).
-detach

# Disable IP address negotiation (with this option, the remote IP
# address must be specified with an option on the command line or in
# an options file).
#-ip

which becomes:

        # Don't request or allow negotiation of any options for LCP
        # and IPCP (use default values).
    #-all

# Disable Address/Control compression negotiation (use
# default, i.e. address/control field disabled).
-ac

# Disable asyncmap negotiation (use the default asyncmap, i.e.
# escape all control characters).
#-am

# Don't fork to become a background process (otherwise pppd
# will do so if a serial device is specified).
-detach

# Disable IP address negotiation (with this option, the remote
# IP address must be specified with an option on the command
# line or in an options file).
#-ip

Comments? Indented?

10 Comments

No need to indent. Just put a blank line after the comment and before the code.

Given this example:

# Bless ourselves into the desired class and perform any
# initialization.
bless $self, $class;
$self->initialize();
return $self;

I would format the comments like this:

bless $self, $class;
$self->initialize;
return $self;

If you use relatively short functions and blocks, and name functions and variables sensibly, comments rarely add anything.

Don't add comments to explain what Perl built-ins do; that's what books are for. Don't add comments to explain your API; that's what pod is for. Don't add comments to explain the meanings of calls to the imported functions and classes you're using; that's what their pod if for.

Reserve comments for the times when you really need them to be as obtrusive as possible - a note about a spec violation you need to fix in a future version; a mind-boggling optimization; an explanation that your code does step 1, step 3 and step 2 in exactly that order because... etc.

PS: one good reason to avoid unnecessary comments is that if you have too many comments you tend to forget about them. When you update some code, you might forget to update the comment, and they fall out of sync.

From your example:

# Any remaining arguments are treated as initial values for the # hash that is used to represent this object.

... but they're not! They're passed to another function, the result of which is stored in one key of the object's hashref. The comment is misleading, and makes it harder for people to understand what the code is doing.

If comments are rare in your code, that's less likely to happen.

If you use relatively short functions and blocks, and name functions and variables sensibly, comments rarely add anything.
Code tells you how, while comments tell you why, e.g. # sort values first to increase floating point accuracy sort @x; $x += $_ for @x; I guess you could encode the comment in the variable names, but IMHO explaining what you're doing is better.

(Oh, FFS, I misspoke the incantation to make code display properly here, and can't ninja-edit to fix it. Sorry.)

# Bless ourselves into the desired class and perform any
# initialization.

I'm guessing this is an example...reminds me of seeing a comment like this in an old assembly language listing:

LD A, B ; load register A with contents of register B

Comments should "almost" never be related to the algorithm (IMHO) and should be restricted to the business rules being implemented. If you have to explain your algorithm, you probably ought to consider recoding it for better maintainability or write a white paper or article about your clever algorithm.

As far as indents are concerned I find that distracting and gives me the urge to do a "M-x indent-region". ;-)

White space is the answer to isolating things you want people to see, again IMHO.


Since i read Perl Best Practices (pages 23 and 142),
i write code in paragraphs, with a short one-line comment before each paragraph.
I find it really useful.

I keep most paragraphs under 5 lines, and most methods under 5 paragraphs. The comments tell a story.

Sticking to this format, the code is bound to be readable. It helps me :
- remember the logic of an algorithm, by reading only few lines of a well-expressed story,
- find faster, which part of the code i need to modify for a specific purpose,
- identify clogged part of the code easily,
- know where to refactor into sub-methods,


Anyone who reads my code should fast get a good idea of what it does, without needing to understand all the details of the implementation first; and even when i haven't found the perfect name for each method - which i try to do anyway, but it's never perfect.

PS: like educated_foo, i use comments only to express the purpose of the paragraph. Not implementation details. Reading only the comments, you should understand what my code does.

The way I see comments is: they are not there to say what the code is for or is supposed to – that’s what documentation is for; they are also not there to say how the code does it – the code itself should make that clear; but if after understanding what the code is for and how it goes about, your first thought would be “why on Earth does it do it this way, when it would be so much more understandable/less work/more robust/etc to do in this more obvious way”, and if the original author of the code had a good reason not to do it that way – then there has to be a comment there to answer that question: why it wasn’t done in that more obvious way.

Yes, that means comments will be rather rare – at least in well-engineered, well-written code. Basically they should be thought of as a hazard attention sign on the road.

Or to put it another way, the point of a comment is to stop the next maintenance programmer from trying to “improve” the code without awareness of whatever specific reason was behind the strangeness.

(There is also a converse at work here: it tells the maintenance programmer to check if that reason still holds, and if not, to clean up the code accordingly. The trivial case of this is the well-known FIXME.)

Leave a comment

About Luc St-Louis

user-pic I'd blog about Perl.