Articles / /* You Are Expected to Unde…

/* You Are Expected to Understand This */

Comments sprinkled liberally through your code can be a godsend when someone else tries to understand what you've done. Better still, they can save you hours of time when you look at it yourself six months later. Unfortunately, including certain types of comments is as bad as not having any at all. Andrew Arensburger shares his thoughts on how to comment constructively.

The Art of Commenting

Rob Pike, in his essay "Notes on Programming in C", says that he tends to err on the side of commenting less, rather than more. There appears to be a school of thought that has taken this one step further, and believes that comments are at best a necessary evil, and that good code should be self-evident enough to obviate the need for comments.

Bull.

Yes, you should be writing code that's clean enough that you don't need to explain what it does. But the code only tells you what the code does; it doesn't tell you what the code was intended to do, what it ought to do, what it doesn't do, or why it looks the way it does.

Bad Comments

The canonical example of a useless comment is:

i++;		/* Increment i */

What makes this a bad comment? Quite simply the fact that it doesn't add to the reader's understanding of what's going on. Compare this with:

for (i = 0; i < num_elements; i++)
{
	frob(elements[i]);
	if (elements[i] == 0)
		i++;		/* Ignore the next element */
}

In the first case, the comment tells you exactly what the code does, but you knew that already from reading the code. In the second case, however, the comment tells you, in human terms, what the statement is intended to accomplish. This is a minor, but crucial difference: the code tells you what the code does; the comment tells you what the code is supposed to do. If you wanted to change the code so that it used a linked list rather than an array, you would know how to translate that statement:

for (elem = elements; elem != NULL; elem = elem->next)
{
	frob(elem->data);
	if (elem->data == 0)
		elem = elem->next;	/* Ignore the next element */
}

Comments as Section Headers

Take a look at a good reference book. If you wanted to use this book to answer a question, you might start by looking up a key word or two in the index, or by finding a promising chapter in the table of contents. Then you would leaf through the chapter, reading section titles and table captions, until you found a page that was likely to hold the answer to your question; then you would start reading the actual text. Without the section headers, it would take much longer to find the part that you are interested in.

In order for a program to serve as its own reference manual, it should contain chapter and section headings, comments that briefly say what the code that follows does. This allows the reader to skim the comments and skip to the part that he's interested in.

Error-Checking

Real code does a lot of work unrelated to its primary task, such as error-checking, assertion-checking, context-sensitive help, and so forth. This can add significantly to the length and apparent complexity of your code. For instance, in ColdSync, a single printf() statement grew to over 17 lines of code once error-checking had been added.

In situations like these, it is especially important to leave section-header comments, lest the reader lose sight of the forest for the trees. Clearly mark what is important and what is incidental.

Write Comments First

Perhaps the easiest way to make sure that your code has useful section header comments is to write them first; before you write any actual code, write, in comments, an outline saying what the code will do:

int
authenticate()
{
	/* Find out which authentication method to use */
	/* If it's a network connection, authenticate host */
	/* Prompt user for password */
	/* Verify supplied password */
	/* If it doesn't match, raise the alarm */
}

Then, when you actually flesh this function out with code, your outline comments automatically become section header comments.

Writing such an outline carries an additional benefit; it allows you to catch, at an early stage, problems in the design itself. The human brain is a wonderful thing, but it is also a result of three billion years' worth of "good enough" implementation, and backwards-compatibility back to the earliest chordates. As a result, it is a giant hack with more than a few quirks.

One of these quirks is that the different parts of the brain don't always work together. You may have experienced "confessional debugging", in which you ask a coworker for help with a problem, but the act of articulating the problem into words or drawing a graph on a whiteboard suggests a solution. The part of your brain that sees the code is only a few neurons away from the part that can fix the problem, but the shortest path between them often leads through the speech centers, out of your mouth and back in through your ears.

Writing an outline is a variant on confessional debugging; by writing a compact, high-level summary of a piece of code, you help ensure that the design is good, and that you haven't left anything out.

XXX

Oftentimes, when the creative juices are flowing and you're churning out code as fast as you can type, you'll think of something that needs to be done in the production release of the code, or in the next version, or just a nifty feature that it would be nice to have.

In these cases, stop and add an XXX (or FIXME) comment:

fd = open("myfile", O_RDONLY);
        /* XXX - Error-checking */

For one thing, this tells anyone reading your code that it's still unstable, and also points out where the known problems are. For another, if you don't mark the problems now, they'll be a lot harder to find a week or a year from now, when you're ready to revisit your old code.

Open Source Projects

If you are writing an open source project, you expect people to look at your code, submit patches, and generally help you to improve the project. You should help these contributors. One of the most damning criticisms of the Mozilla project was that it was very hard to find one's way around it. Don't make the same mistake.

Why would anyone even consider contributing to your project? In most cases, people just want to make one or two small, specific changes; perhaps they want to fix an annoying core dump or add a useful command line option. They want to find the relevant section, fix the problem, and submit a patch. Get in quickly, get out in less than an hour.

You should encourage these people. How? By making it easy to find the problem spot quickly. How? Clean code is a must, of course, but good commenting practices can also make the task much easier.

Section-header comments (see above) tell the reader what a given passage does, and allow him to get a feel for the layout of the project before diving into the code itself. Cross-references, e.g.

int
parse_line(FILE *infile)
        /* This is used by (*parser->lang)() */
help to show how things are connected.

The bottom line is that if your code is too hard to read, people will find it easier to a) do nothing, b) submit a bug report and expect you to fix it, or c) switch to some other program that does the same thing as yours but is easier to hack.

Objections

The main objection to copious commenting, which Mr. Pike raises as well, is that if the comments and code repeat each other, you run the risk that comments and code will drift until they no longer bear any relation. Hence, some conclude, it is better not to comment.

To me, this sounds like saying that a highway construction crew might forget to update the road signs when necessary, so therefore there shouldn't be any highway signs. Of course this is a risk, but in the vast majority of cases, it is nice to have signs that say where the road goes, and roads that go where the signs say.

If you use tables of data in your program, you still have to maintain them, even though the compiler can't tell you whether they're wrong. They're just as much a part of your program as the actual code. So it is with comments. Don't just maintain code. Maintain code and comments.

Commenting Out Code

Just for completeness, I'll also point out that commenting out code is a good way to temporarily delete code that you'll want to return to later. I'll only point out that Perl- or C++-style comments allow you to have comments inside comments;
# This is a comment
# <old code> # old comment
whereas C-style comments do not;
/*
This is a comment
<old code> /* Old comment */
this is not commented
*/

Consider this the next time you're designing a language.

Conclusion

Good commenting practices can make code cleaner, easier to understand, easier to debug, and generally more fun to hack. And isn't that why we write code in the first place?

Recent comments

26 Feb 2006 02:46 Avatar thequux

Re: I used to not comment....


>

> But now I love to. I tried it one day

> and I'll never write uncommented code

> again. I comment so much its crazy, and

> thats a good thing I think.

>

> Yeah I used to write awful spaghettie

> code with no indenting, no comments,

> nothing.

>

> But now I don't understand why anyone

> wouldn't comment. Unless you type 20WPM

> it won't take more than a few seconds to

> add a good comment to eaach line, and

> it'll save you at least 10 times that

> amount of time, not to mention the money

> you will essentially save.

>

> I started working on someone else's open

> source project, and he just doesn't

> comment. You get about 5 words at the

> top of some of the biggest functions.

> 500 lines of code with nothing but

> &quot;Parse the file&quot; as a

> comment.

>

> I comment nearly every significant line

> with a useful comment, not something

> that explains the command. I always

> explain the logic behind the code. Weird

> things that people need to know - I

> explain. This often means things like 4

> lines of comments for something as

> simple as &quot;exit;&quot;.

>

> Each program also has a nice explanation

> at the top and I use lots of headers and

> comments as dividers between big

> functions.

>

> My goal is for any programmer (including

> myself) to be able to understand the

> logic of my code the first time through.

> Having to read through someone's code 15

> times to get an idea of what's going on

> is NOT good. With little shell and perl

> scripts that are 30 lines that's not a

> huge problem, but when you're writing

> huge applications, good luck even

> keeping up with it once its 10s of

> thousands of lines long if you didn't

> comment.

>

>

>

Actually, then there's my ad-hoc C/C++ code.

There's just something wrong with something that looks like an entry to the Obfuscated C Contest, except it breaks the length restriction, especially if it contains /*ermm...*/ embedded in the 22nd line as the only comment visible on my 1600x1200 screen using a 12 pixel font.

Even worse (with the code in question), is that the code would just not work if the comment was chamged, added, removed, or just plain moved.

At least the code worked (I think)... but I have never done anything that bad again.

18 May 2001 04:16 Avatar chriscog

Re: Comments and style

> Um... no.
>
> The whole point of my comment was that
> tools exist that allow everyone to use
> their own style. I'm know very little
> about Python. But the fact that it uses
> indentation for blocking is irrelevant
> to my point. I'm sure Python
> programmers still have their own style.
> Maybe there are no formatting tools for
> that language. Maybe there should be.
> Whatever.
>
> I appologize for being so specific.


Agreed... I dont know what I was smoking when I wrote that :)

07 May 2001 14:24 Avatar suitti

Re: Harmful comments?
Well, I don't think there's any
possible way that a comment could be
"harmful"


Unterminated comments have, on occasion,
resulted in code that compiled, but did not run.
It can be hard to locate such defects. A comment
stripper can help. I have one (for C) that can show
just the code, or just the comments. I would not
have written it if I didn't need it.


I use #if for commenting out code, since
they can be nested, and don't interfere with
comments (which in C don't nest).


I've seen very old C code (predating the C preprocesser) that
used if (0) { ... }, which the compiler recognized,
and would omit (if there were no labels in the block).

07 May 2001 14:01 Avatar suitti

Re: NASA and the Space Shuttle flight programs


Apparently each line of the space
shuttles flight
program has something like a full page
of documentation.


Every bit of real code has an
explanation of what
it supposed to do, and some sort of
mathematically
rigorous proof of why it's correct.


The result: apparently they have had
just 17
runtime errors of any nature at all
since the
inception of the shuttle program.


They estimate that there are about 50 bugs
remaining. Not bad for 500,000 lines of code.
But, they spent about $20,000 per line of code.
This is about as much as the hardware.
And, all process aside, an audit suggested that
the real knowledge of why the thing works
the way it does is passed by word of mouth.


If what you want is bug free code, looking at
the shuttle code processes can be of help.
Testing is also required. Judgement is also required.


If you want understandable code, you need
documentation at more than one level. The
'skip next record' example shows documentation
at a slightly higher level of abstraction than the
code. Section comments are slightly higher
still. A function header can (with proper judgment)
describe a slightly higher level. Often, comment
levels above this are entirely omitted. Thus,
there often is no 60,000 foot level overview,
outside of the user documentation (if any).


Historically, Berkeley 'tar' had something like
three comments for 720 lines of code. One said
that this was 'tar'. Another was of the 'increment x'
class. The third commented out a line of code.
Yet, the result was pretty easily followed, despite
it's unique handling of command line arguements.
The user documentation (man page) told you what
the program did (though not why you might want it).
Sparse commenting for small projects should be
examined.


My commenting style is pretty verbose. I try
to answer the obvious 'why' questions. Why
would someone want this program? Why was this
function written? Why was this unusual code
written? The code answers most other questions.
I assume that the reader knows the language,
and try not to duplicate the code in the comments.


If the comments don't match the code, then
it is likely that both are wrong.

18 Apr 2001 06:41 Avatar Juerd

This #
#!/usr/bin/perl
# this is a perl script
# this is a comment
# this used to be a blank line
while # loop
&nbsp;&nbsp;&nbsp;&nbsp;(1) # forever
&nbsp;&nbsp;&nbsp;&nbsp;{ # begin block
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print "Hello World\n"; # output `Hello World', and a newline character
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep 5; # sleep five seconds
} # end block
__END__
This program forever loops a block that outputs `Hello World', with 5 seconds time between output commands.

Screenshot

Project Spotlight

Kigo Video Converter Ultimate for Mac

A tool for converting and editing videos.

Screenshot

Project Spotlight

Kid3

An efficient tagger for MP3, Ogg/Vorbis, and FLAC files.