ACCU Home page ACCU Conference Page
Search Contact us ACCU at Flickr ACCU at GitHib ACCU at Google+ ACCU at Facebook ACCU at Linked-in ACCU at Twitter Skip Navigation

pinComments Considered Good

Overload Journal #74 - Aug 2006 + Programming Topics   Author: William Fishburne
In our last issue, we offered the case against comments; in this issue, William Fishbourne responds in defence. He argues that it is not the practice of commenting that it is evil, it is the thoughtless commenter.

At the ACCU 2006 conference, the question was asked, "Is the total value of all the comments in all of the code out there less than or more than zero?" [ACCU2006] Mr. Easterbrook [Overload73] concluded that if the answer is less than zero, then it must be the practice of commenting that is at fault. Consider, for a moment, the same question with a different subject, "Is the total value of all the punditry in all the newspapers out there less than or more than zero?" If one could conclude that the pundits of the world offer little or detract from the world, following this line of reasoning, should one conclude that freedom of the press is inappropriate? Would it not be better to encourage more writing in hopes of finding better pundits? In a like manner, this author encourages more commenting for better commenters, not the elimination of comments.

Learning from the experts

Mr. Easterbrook took a look at the comments that experts put in books for beginners and found the comments woefully inadequate. This is an unfair test, however, as the books for beginners use comments to explain simple concepts to beginning programmers. A look at the complexity of the programs in beginning programming books would likewise be found wanting. Let us consider, however, how experts comment code designed for the advanced programmer. Stan Lippman in Essential C++ [Lippman2000] has this example:

template<typenamevalType>
voidBTnode<valType>::
lchild_leaf(BTnode*leaf,BTnode*subtree)
{
  while(subtree->_lchild)
    subtree=subtree->_lchild;
  subtree->_lchild=leaf;
}

Mr. Easterbrook's argument is made! The code is, however, dense and while it is possible to figure out that BT probably means 'Binary Tree', especially with the help of lchild which is probably 'left child', it isn't intuitively obvious what this function is doing. After all, lchild could be 'least child' or BT could mean 'Branch of the Tree'. All in all, however, the code is easy enough for an good programmer to understand. Some might argue that longer or more descriptive names might make understanding easier, those same people might argue that such long names are hard to type in and lead to typos which are false errors. Mr. Lippman has chosen to comment upon invocation where explanation is useful:

// lchild_leaf() will travel the left subtree
// looking for a null left child to attach...

These comments are useful and help elucidate the manner in which the function is being used.

Computers have often been compared to "universal tools" and procedures have also born this title. To the extent that a given procedure is generic or universal, the invocation of that procedure should be accompanied with comments that (as Mr. Easterbrook points out):

  • Say why and not how
  • Are meaningful
  • Used where the code is non-obvious and/or non-portable

Tying the comment to the invocation, in this case, helps facilitate understanding of the code at the point where that understanding is needed.

Is code self-documenting?

Simply put, no. As an advocate of self-documenting code, this author is frustrated to admit it, but the simple fact is that code is not self-documenting no matter how hard a programmer may try. As the first example shows, even code that uses meaningful names in an intuitive manner can be very hard to understand.

Consider this snippet from Marshall Cline's Essential C++ [Cline1999]:

classstack{
public:
  stack()throw();
  unsignedsize()constthrow();
  inttop()constthrow(Empty);
  voidpush(intelem)throw(Full);
  intpop()throw(Empty);
protected:
  intdata_[10];
  unsignedsize_;
};

Now consider the comments recommended by the author:

intpop() throw(Empty);
  // PURPOSE: Pops and returns the top element from
  // this
  // REQUIRE: size()must not be zero
  // PROMISE: size() will decrease by 1
  // PROMISE: Returns the same as if top() were called
  // at the beginning

The comments tell us what to expect from the member function and this type of information is critically important for interfaces, etc. While separate test functions might prove this functionality, it is worth taking the time to make these comments so that someone using the member function would be able to rely upon this functionality.

The comments offer us something that code alone cannot. There is no way for the declaration to state that size will decrease by 1 as a side-effect. One may argue that such side effects are components of bad code and be right in many cases, but that argument does not solve the problem - the code cannot document itself, particularly in side-effects.

Comments as design reference

Mr. Easterbrook refers to the use of pseudo-code as the programmer opting to code in another coding language to which the programmer has a preference. Many consider the use of pseudo-code a critical design step. In fact, it is sad that so many programmers have abandoned the practice of actually designing code before implementing (or, perhaps more correctly: hacking) it. Whatever the nature of the design process, it is worthwhile capturing the design as comments: first, as a reference for the programmer as the program is developed, and, second, as an insight into other follow-on programmers who are trying to fathom the whys and wherefores of a particular program.

The first code snippet given, might have been created in pseudo-code as:

// template static functionl child_leaf
// traverse the left subtree looking for a null leaf
// to attach
// accept a leaf and subtree as parameters
// while the left subtree exists
// step down the left subtree
// put the passed leaf into the left subtree

As comments, things like "while the left subtree exists" may seem redundant to the code that was written; however, it reflects the design process and the comments (with code removed) offer insight into the algorithm used. In this way, even "obvious comments" have a place, sort of like the paragraph headers which appear in this article. The paragraph headers aren't strictly necessary, the structure of the article should be self-evident, but the headers facilitate the reading of the article which is, after all, the point.

Comments that are used as part of the structure for writing a program should be kept in the program and not simply removed as the components are written. These comments help guide the reader through the code and help the programmer develop well-organized code which doesn't leave out crucial missing parts.

By the same token, comments should be maintained in the same manner as the code itself should be maintained. As the algorithm changes the comments, as well as the code, that was outmoded should be revised or removed. The examples given by Mr. Easterbrook are sad examples of code maintenance and simply another example of why peer code reviews are necessary. Think for a minute, would you let a peer slide on comments that are misleading or incomplete? It is in everyone's best interest to prevent that from happening.

Mr. Easterbrook has some great ideas

Compiler aware annotations are an interesting innovation, although there are still plenty of people who don't code in some graphic environment where annotations could pop up over the code itself. Mr. Easterbrook's concept of compiler aware annotations has hit on an improvement that only lacks an implementation. Perhaps some innovative programmers will get together and add this type of functionality to gcc and the programming environment of their choice. At the risk of inciting a riot, it seems that this would be easier to do with EMACS then with vi!

The call for meaningful comments is a good one, although banning headers is as bad as requiring them. Headers have a place (just like every book has a title and author on the cover page), but the place is to facilitate the reading and understanding of the code, not simply to meet a check box. When programming becomes a "step in a process" the art has been left behind and shoddy, cookie-cutter code is the result. Innovative thinking, like that of Mr. Easterbrook, is the hallmark of good programming, whereas meeting a series of check boxes is hardly short of automation.

As a modification of Mr. Easterbrook's call to action, leaders of code reviews should ask themselves why the comment would be useful. Perhaps most of the staff that maintains the code is composed of junior programmers and a comment that an experienced programmer would find unnecessary, should be there for the junior programmers. Perhaps there is a component of business logic that (though obvious) needs to be stated as such so that it is not later "mildly" modified without consideration for the importance of the business rule embodied in the code. Finally, the thinking process of the programmer can be documented in the comments, which is a singularly unique insight into why things are programmed in a specific manner as opposed to simply noticing that a particular path has been used.

Please remember, comments don't make code bad, programmers do.

References

[ACCU2006] By Russel Winder in Peter Sommerlad's session called "Only the Code tells the Truth".

[Cline1999] Cline, Marshall, et al, "C++ FAQs Second Edition, Addison-Wesley, 1999, ISBN: 0-201-30983-1

[Lippman2000] Lippman, Stanley C., "Essential C++", Addison-Wesley, 2000, ISBN: 0-201-48518-4

[Overload73] Easterbrook, Mark, "Comments Considered Evil", Overload, Issue 73

Overload Journal #74 - Aug 2006 + Programming Topics