Mistake #1: Doing them (comments)

Introduction

Ack!  That's right. Jot my name down in the list of 'Programmers who publicly acknowledge that [most] code comments are bad'.

I was reading Sunil Bhatia's post entitled '12 Common Programming Mistakes to Avoid' and I started writing a long-winded comment to reply, but I thought that this was best done properly in a blog post.

First, before we begin, I strongly think his list could be boiled down to #5 and #12, the rest will fall into place or become irrelevant.  Such has been the case for me personally and everyone I know who has tried it this way.

Second, the problem with comments. Right, here  it is:

Let's start with a link for background/context: Code comments: Tightly coupled and poor a way of saying "I'm Sorry" to a maintenance programmer [Gary Sherman]

Along those same lines, my problems with code comments are these:

  1. Comments are tightly coupled to the code they describe, but are not checked by any compiler or interpreter. There is no enforcement on whether the comment has any bearing whatsoever on the code it describes
  2. No enforcement of usefulness. Code is executable and, therefore, has inherent usefulness (whether it produces correct results or not is another matter, but at least you can execute it). Comments have questionable, or subjective usefulness and are usually just noise, distracting a would-be maintenance programmer from finding the info he/she needs (i.e. where the code is going wrong). In my experience, comments are of negative value and often serve as Red Herrings when I'm trying to debug or sleuth out a problem.
  3. Tend to be editorial, pejorative, or just plain insulting -- therefore contributing nothing to productivity (covered under the previous bullet, but this one needs a special call-out).  Frequently programmers use comments to vent frustration as evidenced by the 'Linux Kernel Swear Word Count'.  Though most programmers are usually a little more subtle and expose passive-aggressive insults at the source of a particular requirement (i.e. '//FIX: #9847 User seems to think it's better to return a banker-rounded number here instead of a properly-rounded number. 5th grade must have been a blur to them')
  4. They are usually obsolete the second they're written. Code is fluid, but generally comments are treated as second-class citizens and are not maintained the way the code is. So comments end up rapidly falling out of date and end up being flat out wrong. Generally, maintenance programmers are trying to fix the code and don't understand what a particular function does (and I'm sure the comments help very little anyhow) and so they don't know enough to update the already-confusing and inconsistent comments. So the comments rot.
  5. This is another play on the first problem (tightly coupled), but worth mentioning [hat tip: Ray Houston]: One of your goals is to be able to get to a point of change confidence (though unit testing and proper decoupled design) that you can 'Refactor Mercilessly' -- that is, refactor what you need to, when you need to, without fear of breaking everything or having to double-check everything that your refactoring tool did.  In order to achieve this level of confidence you need: a.) Great tests b.) Well designed code and c.) A great refactoring tool.  This is the panacea of rapid, agile development. It's difficult, but very possible to achieve. By having tightly coupled code comments, you essentially defeat yourself and necessarily require yourself to walk back through the refactoring that your tool just did and ensure the comments are all up to date. 

To summarize: I feel that the best gift you can give to a maintenance programmer is the confidence to make wide, sweeping changes with great confidence. This is infinitely more valuable than code comments. As Gary said above, "Comments are a way of saying 'I'm Sorry' to a maintenance programmer."  Stop saying your sorry, and start helping that poor slob the best way you can.

I'm sure all sorts of questions are popping up right now in your head. I'm going to try to address these one by one:

I'm not saying documentation is bad

I'm not saying documentation is bad. Oh no, documentation is good. But documentation MUST NECESSARILY BE MAINTAINED AS A FIRST-CLASS PRODUCT just like the code and the tests. I think we can all agree on that point at least, right?  If that's the case, then why make your life 10x harder by scattering documentation artifacts all over, mixed in with your executable code? This serves nothing and only ends up confusing both code and comment alike. Keep your documentation where it belongs. Use a tool which helps you manage your documentation library and even, maybe, keep it somewhat in sync with your code (by checking for now-defunct links to classes or methods, etc).

In some cases, unfortunately, comments make sense

One of these cases is XML API comments in C# and VB.NET (or javadoc-style comments in Java).  API documentation is important, and it helps to keep it close to the code it describes. I cringe though because they're still not completely compiler-enforced, so you're back to square one.  But at least, from what I've seen, developers take these seriously and make serious effort to keep these up-to-date with the code.  This is good, and a proper use of comments to the maximum usefulness possible.

How to do without code comments and still make your successor not want to strangle you

The goal of commenting code is to describe to a future maintenance programmer what the intention is of the code being commented.  If that is truly the goal, which I believe it is 99% of the time (minus XML API comments, license headers, etc) then you will serve your fellow man (or woman!) much better if you simple make your code expressive and self-explanatory.

Write beautiful, expressive, self-explanatory code and rid the need for self-loathing comments

Consider this example of code commenting:  'Successful Strategies for Commenting Code', specifically the last example (the 'beginBattle()' one, reformatted to be 'proper'). In this example, an attempt is made to reformat the code and comments to reduce comment clutter while still being verbose enough to describe the intention and effect of the code. I say, "Why stop there?"  This seems like a half-way point in the reformatting of this code. Why not go all the way and remove the need for all of the comments in the first place? Here's what I would do, in this case:

function determineOrderOfAttacksAndProcessEach( attackingCreature, defendingCreature ){

    defendingCreature
        .when( defendingCreature.initiativeGreaterThan(attackingCreature) )
        .attack(defendingCreature);
    
    attackingCreature
        .when(attackingCreature.isAlive())
        .attack(defendingCreature)
        
    defendingCreature.team.forEach(new function(teammateCreature){
        teammateCreature
        .when( teammate.wantsToCounterAttack() )
        .attack(attackingCreature);
    });
    
    // TODO: Process the logic that handles attacker or defender deaths
}
 

Now, to the die-hard commenter, I ask you: How and where would you add comments to this function that would, in any way, improve its readability? Note that I left the TODO in there. This is a useful comment. It conveys something that cannot be conveyed (yet) in code. It's also an artifact that tools (like VS.NET and R#) will pick up and help you keep track of. It's also a standard and is generally not as subjective or editorial as normal comments would be. It's not tightly coupled because it doesn't represent any code, only an idea or suggestion for future implementation. This is the natural place for this type of documentation (although you could have it in a wiki somewhere or it's probably covered by a story or requirement in a spec somewhere).  The 'scattered all over the code' argument applies here, but, like I said, tools like VS.NET and R# will help you locate and manage these.

Final Thought: Let the code speak for itself

Beautiful, expressive, self-explanatory code is the best way to convey the intention of the code. Why not let the code speak for itself?  As a side effect, it's also almost always easily-testable and well designed, decoupled code.  Since more thought was put into its overall design and more consideration was paid to things other than just 'does it work?' (like 'is it testable?', 'can I reuse it?', 'does it make sense to someone other than me, or me 6 months from now?', etc), this code fits almost all the goals we're striving for in a well designed, well organized, highly expressive API without compromising the documentation at the same time.

[EDIT: Typo fixes, a few grammar fixes, added the section on comments + refactoring (#5 in the 5-point list)]

Technorati Tags: , , ,