Thursday 22 September 2011

TODO or TODO Not - There Is No Later

The “TODO” style comment is a blight on the modern codebase. Not so much the wizard generated ones that MFC programmers never bother to remove, but ones blindly added in some vain hope that they are going to be understood and then actioned by some other programmer in their spare time. I once remember seeing a comment like this, which through the power of the version control system I discovered had been unanswered for nearly 10 years:-

// TODO: See if this is right.

One would hope that after so long it’s either right or on a dead code path that no one cares about. Hey, how about writing “a test” to answer the question instead of writing the comment? Going back to the wishful thinking style TODO I particularly liked this one I saw in a piece I code I then had to work on:-

catch(...)
{
  // TODO: How to handle errors?
}

I answered the question by removing the catch-all handler and letting them propagate to the caller. Funnily enough this unearthed an occasional error that then explained some other mysterious behaviour. Performance related TODO’s always amuse me because they are so likely to be wrong when the time comes:-

// TODO: Switch to a map if this needs to be faster
std::list<. . .> something;

What are the chances that at the time performance becomes a problem that this comment will still be correct? I would never blindly make a change based on someone’s gut instinct 5 years ago about how the code is likely to evolve. If you’re worried enough that you’ve chosen the wrong container type up front to comment on it then change it. Otherwise let the profiler do the talking.

One of the first things I disable in Visual Studio is the auto-detection of TODO’s being added to the Task window. After all I don’t want to get bogged down in a sea of unscheduled work left by my predecessors. In the modern era of programming where we’re all doing TDD our entire workload is already planned out of the next few weeks in stories and any refactoring will be done along with it. You simply don’t have the time to mark a point in the code with the intention of coming back some time later to finish it off - done has to mean done.

If TODOs in the source code comments are bad enough, some teams go one step further and make use of compiler features like #pragma message in Visual C++ to make sure they also appear in the build output just in case you managed to avoid them in the code. This just makes a clean build output an impossibility as you’re constantly drawn to check out the distracting build message as it passes by each time in case it wasn’t the TODO this time but an important message…

On the last few projects I’ve worked on I’ve instigated a “No TODO can live past a release” policy. This means I grep the codebase just before a release and either promote the TODO to a fully fledged work item if it is still worthy of attention (probably with the lowest priority in the hope it’ll just go away), or more commonly, delete it. So many are just as superfluous as the code comments and will be just as apparent when the code is next visited - refactoring will happen naturally if it needs to be done.

5 comments:

  1. Agreed!

    The catch clause is a beautiful example of the damage todo can do.

    ReplyDelete
  2. Yes, I fully agree and I go further, don'e use TODO ever... TODO or not to do

    ReplyDelete
  3. I mostly agree - but not entirely.

    // TODO: explain why

    ReplyDelete
  4. I do use TODOs (although I call mine !TBD, to make sure they are distinguished from those that may be generated).

    As a general rule I only use them as I'm writing code and I know I need to add something, but it's not necessary to get the code running/ test passing right now.
    It's at that point that I have the pieces in my head to know what should go there, but if I was to do it there and then it would break my flow - and be superflous to the task at hand. If I don't write it as a !TBD then I'll lose valuable context that must be regained when I do have to come back to it.

    Of course when I do come back I may have a valuable new context, or updated knowledge, that invalidates or extends what I originally wrote - but that's ok. I now have the benefit of *both* contexts to make my decisions on.

    I try not to check !TBDs in. This rule could be stricter, but in practice it's easier to check in as soon as my immediate task is complete - !TBDs and all - then, as a follow-on go back and review the !TBDs I introduced. Either convert them to tests, fix the code, log an issue or whatever.

    That's the theory - and it bears some similarity to reality.
    In practice, of course, that would take far too much discipline! So crufty !TBDs can tend to build up. I usually bump into them soon enough - either accidentally, or by grepping for !TBD - and either rip them out or follow one of the other practices I mention. So they don't hang around for too long.

    Except when they do.

    ReplyDelete
  5. Guilty...

    and I have optional build time noise via a custom #pragma (which has been turned off in most of my builds for a long time now, enough said)...

    I now try and include the JIRA ticket number in the TODO, and I try and cull them at various 'stage complete' points in the development.

    ReplyDelete