Code in Comments

2004-06-07, Comments

Introduction

We have all seen comments in source files which look more like executable code than documentation.

The first line in the body of the for loop below is such a comment: you might expect to be able to remove the leading slashes and have code which compiles and runs but functions slightly differently.

What did the author of this comment intend?

Example 0
for (Surfaces::iterator sf = surfaces.begin();
     sf != surfaces.end(); 
     ++sf) {
     // std::cout << "Drawing: " << *sf << "\n";
     sf->draw();
}

OK, I’m being disingenuous. I’m aware that the comment isn’t really a comment, it’s commented-out code. And, like any tolerant and capable programmer, by examining the surrounding context I can guess why this code has been commented out.

This article examines how to comment-out code, then describes various problems which lead to code being commented out, before finally arguing that there’s often a better solution to these problems.

How to Comment-Out Code

Usually it’s as simple as using your editor to select a region then instructing it to comment out that region.

If using C-style comments — by which I mean comments delimited by /* and */ — then bear in mind they do not nest, so you may run into problems with real comments in the code you want to comment out, or even with commenting-out code which has already been commented-out.

I came to C++ from a C background, and remember attending a training course at which the presenter pointed out how easy it was to comment out C++ comments using C comments:

/*
for (Surfaces::iterator sf = surfaces.begin();
     sf != surfaces.end(); 
     ++sf) {
     // std::cout << "Drawing: " << *sf << "\n";
     sf->draw();
}
*/

Oh dear!

If your editor’s syntax highlighting makes it obvious that the whole loop is commented out, great. Would it still be obvious if, for example, you were paging through the source file on a remote computer? Or if you were code-reviewing a printed version of the file? Or if you had hit a commented-out line while searching? Or even if you were at a customer site and didn’t have access to your favourite editor?

The more useful thing to say about C++ comments is that they run from where they start until the end of the line, and therefore do not suffer from this nesting problem. So a C++ comment can be commented out by a C++ comment!

// for (Surfaces::iterator sf = surfaces.begin();
//     sf != surfaces.end(); 
//    ++sf) {
//    // std::cout << "Drawing: " << *sf << "\n";
//    sf->draw();
// }

If using C-style comments, then the following style makes it clear which lines are part of the comment:

/* for (Surfaces::iterator sf = surfaces.begin();
 *     sf != surfaces.end(); 
 *    ++sf) {
 *    // std::cout << "Drawing: " << *sf << "\n";
 *    sf->draw();
 * }
 */

If your editor does not allow you to easily comment out a lengthy region of code in this way then either use a better editor or read the rest of this article and consdier whether commenting out the code is really what’s required.

A more heavy duty way to stop a block of code from executing is to instruct the preprocessor to skip past it.

#if 0
    for (Surfaces::iterator sf = surfaces.begin();
         sf != surfaces.end(); 
         ++sf) {
         // std::cout << "Drawing: " << *sf << "\n";
         sf->draw();
    }
#endif

If this technique is used, the preprocessed-out code blends perfectly with the executable code. Even syntax-highlighting does not expose the fact that the code will not be executed.

More Examples

Example 1
void Session::registerClient(Client const & /* client */) {
    // if (!registered(client)) {
    //     m_clients.push_back(client)
    // }
}

Example 2
switch (table_selector) {
    case table0:
         /* convertTable0(data);
         break; */
    case table1:
         convertTable1(data);
         break;
    ....
}

Why Comment-Out code?

The obvious answer — to stop it from executing — begs the further questions: Why should the code not execute? Did it ever execute? Will it ever execute? There are several possibilities:

  1. The commented-out code has been superceded by something better, but the author of the new code wants the old code to stick around for a while, perhaps as a reference, perhaps out of historical interest, or perhaps because the new code might not turn out to be better after all.

  2. Commenting the code out appears to fix a bug, but noone understands why — hence the old code is left in comments.

  3. The code was commented out during an experiment, maybe an attempt to reproduce a bug, and should never actually have been checked-in in such a state. In other words, a bug has been introduced: in fact the code should still be executed.

  4. The source file belongs to a third party library which has required modifications to work in-house. The lines which have been commented out represent the source code in its original form.

  5. The commented-out code produces irritatingly verbose debug diagnostics which needed silencing.

  6. The commented-out code represents work-in-progess which the author has sensibly checked in to the source repository for safe keeping.

These are all reasons for turning code into comments, some more reasonable than others. Categorising our examples: Example 0) appears to be silenced debug, Example 1) work in progress, and Example 2) a hacked-up experiment — though we really cannot be sure. Example 2) might equally well remove the symptoms (if not the cause) of a bug and Example 1) might represent a superceded method.

The important point is that unless the author of the commented-out code explains what’s intended, it is impossible for future readers to accurately guess. How, then, should the author explain? With a comment!

for (Surfaces::iterator sf = surfaces.begin();
     sf != surfaces.end(); 
     ++sf) {
     // std::cout << "Drawing: " << *sf << "\n";
     // Work in progress. Surface stream output not yet implemented.
     // Thomas Guest, 12-Dec-2003.
     sf->draw();
}

Depending on circumstance, the comment might read:

// Do not inflict verbose debug output on everyone.
// Thomas Guest, 12-Dec-2003.


or even:

// Commenting out the above line of code appears to cure the
// random crashes reported as PR666. I am not yet sure why,
// but am leaving the code commented out while I investigate.
// Thomas Guest, 12-Dec-2003.

I have included an explicit name and date in the comment — redundant data, strictly speaking, since they duplicate information held in the source management system — because I do not intend the code to persist in its current state. The comment has a sell-by date. Anyone reading it will immediately understand what’s going on and who to hassle if they don’t like it.

Why Commenting Out Code is a Bad Idea

Commented-out code — like any other comment — ages badly. It needs maintaining if it is to remain in a state where it can be uncommented and compiled, should the need arise, and of course it won’t be maintained in this way since the need is unlikely to arise. There is more than enough live code to maintain without devoting attention to half-dead code in comments.

Once code has been commented out it becomes hard to remove: someone at some point obviously thought the code worth leaving in, so future programmers working on the file honour that decision, although they may well consider the code smells a bit off — code which someone once found problems with, attempted to cure, never really got to the bottom of, and left for someone else to sort out, or, more than likely, ignore.

What to do Instead

The source management system is the proper home for old versions of source code. If, for some reason, it really seems necessary to explicitly note that an alternative version of the current code once existed, then this can be indicated using indirection.

// Previously, surfaces were stored in a vector, not a list.
// A list is now used to support efficient splicing.
// Refer to version 1.22 of this file for the vector implementation.

There is a direct parallel with the change history of a document: at times it may be very useful to review who changed what, when, or to examine side-by-side differences with the previous version of the document, but this is achieved by accessing the document’s revision history, not by leaving obsolete wording lying around in strike-through style. The up-to-date version of the document should be up-to-date.

Ideally, then, the dead code can be cut away. To achieve this ideal requires proper use of the source management system. Check-in comments should be of the same standard as any other project documentation — they need to be clear, accurate, and must include relevant cross-references (to bug report numbers, for example). An iterative approach to check-ins works well: i.e. take the code in steps towards its new form, checking in after each step is complete.

We may not be dealing with dead code, though. Perhaps the code represents work in progress — functions which are being written and which do not yet work, but which none-the-less belong in the source code repository. In this case, the code needs to indicate why it has been commented out, as already mentioned. An alternative would be to store the developing code on a branch until it matures.

Similarly, if the commented-out code represents the original content of a third-party file, then an in-line explanation is required to make this evident to anyone inspecting that file. Even in this situation, I would argue the original content should be cut and the source control system used to manage the differences.

Debug output often gets commented-out because it degrades performance or fills up screens. In this case, either the debug code really was a one-off, and should be deleted after use, or it should be carefully integrated into the trace system (the trace system being the module which provides the facility to efficiently filter logged output at run time).

Conclusions

What would happen if you were to cut commented-out code from your source tree? My guess is that you would have significantly less code to maintain, that much of the remaining code would be cleaner (and therefore easier to maintain), that old versions of code would remain accessible, and that functionality and efficiency would be unaltered.