On “TODO” Comments

It's smart to break up work into chunks. But they need to be defined chunks.

First off, I’m a fan

I like TODO comments… if they’re for things that will actually be done. It’s neat that they’re common enough that most IDEs will highlight them for you. There a plugins that will gather them in a list for you. All this is an attempt to hold us accountable for putting them in there in the first place. But even with all of that, they’re still pretty easy to ignore. They become little reminders of technical debt. And if you amass enough of them, you just don’t even look at them any more.

But I think a tiny bit of discipline can turn them into something useful. If you’re using an issue tracker like JIRA (or even GitHub issues), then there’s an identification system for them. Say you’re working on a feature, and it’s split into multiple issues: you can generally know when your TODO comment will get done. I think it’s useful to put the TODO comment in there and mention the issue identifier.

But sometimes you don’t know when something is planned or will get done. Maybe you just realized you need something additional for a feature, and it’s too big for this branch. Create a new issue for it and then put that in the TODO. This is a signal to other devs that this isn’t a “hopeful TODO”. It has definite plans and possiblty even a deadline. I have a habit now of doing a ripgrep on a whole codebase for “TODO Issue #…” or “JIRA-…” just to find any places where I (or others) might have mentioned the task I’m currently on.

Sometimes, I’m not a fan

If you’ve got so many TODOs that you’re ignoring them, often times it’s an indication of a bigger problem — especially when it comes to refactoring. If you are going to refactor it, but have decided to only refactor half of it with a TODO to change the rest of it later: either the refactor wasn’t planned well enough or you don’t have enough engineers.

And that’s okay. For startups and small teams this makes perfect sense. Keep in mind, though, that when you do hire those engineers, they will see the half-refactored whatever-it-was and hate it. Usually when an engineer in a new job hates something, they want to rewrite it or kill it with fire, so maybe that works out in your favor. Ideally your existing team will know the refactor that needed to be done and the new engineer would learn more about your system/product by doing it.

Do not to force this on the new engineers. If they get the itch, let them scratch it. If they don’t, then leave/put it on the backlog just like everything else.

The worst way to use them: mythical refactoring

So this is an over-simplified example, but… say your business has a feature for tracking events at venues. You have a module: venues. This module holds things like tickets, shows, etc.

  • src
    • venues
      • tickets
      • shows

But a small percentage of your users are also restauranteurs and start to use the feature for finding all their locations regardless of whether they use the “sub-features”. This seems like a business opportunity, so you pivot. The team refactors venues out and add locations with “Venue” as a location type. But you leave tickets and shows under the venues and just foreign-key to the new locations.

  • src
    • venues
      • tickets
      • shows
    • locations

This was fast enough for you to expand features specific to locations, but it means that tickets and shows have links to venues that are actually now locations, etc.

Maybe there’s just too much functionality wrapped around the “venues” concept and someone might still use it. So you and the team say, “We’ll fix it later.” Then the TODO comments are in the repo for two years.

I hold the opinion that if you had more engineers on the team, other features could be worked on while part of the team was dedicated to doing the refactor to its more logical end. Or you could put those other features on hold. A collection of half-implemented refactors will only hurt more in the future. They are a confusing and terrible technical debt with a “high interest rate”.

But I understand business needs enough. Do you need to do the refactor completely in order to make money? Nope. Probably not.

Do you need to do the refactor completely to have happy engineers and an easier path to develop new features? YES.

So find the trade off that fits in your budget (of either time or money).

PLANNING

I guess it all goes back to having a plan. If you have a plan (i.e. backlog), put the numbers in your TODO comments and help future you colleagues. If you don’t have a plan, make one in your issue tracker, then put the numbers in your TODO comments.

BAD:

def check_venue_permissions(user, location):
    # TODO: optimise this...
    ...

GOOD:

def check_venue_permissions(user, location):
    # TODO OLIV-8079: optimize this permission call using a redis cache (since this complex query is called on every request)
    ...