Skip to main content
Back to blog

Software development

Should you put TODOs in the source code?

Nicolas Carlo
Oct 05, 2020 ∙ 6 mins
Person at the top of a mountain

TL;DR: Use temporary TODOs as you work on a feature, but make sure to treat them before merging. Either add a link to a proper issue in your TODO, or remove it from the code. You should have a single, shared, visible backlog of things to do. Don't let TODOs become an invisible one.

How do you feel about // TODO comments?

The number of TODOs present in a codebase is usually a good (and cheap) informal metric of its maintainability. It gives you an overview of things that are missing or should be changed, which usually goes unnoticed by other metrics that are assessing existing code quality.

The fewer TODOs you have, the better the codebase tends to be. Right?

And yet, leaving a TODO usually feels like a good idea when you do it. In general, a TODO is written to tell future maintainers about something important: something that should be added, or should be changed, or should not be forgotten. The intention is often to increase the quality of the code. Sometimes a TODO comment can save you a lot of time!

Imagine working with a codebase with very few TODOs. You understand every TODO that's in here, and you know when they would be tackled. Estimating the cost of new features is easier: these TODOs provide you with all the context you need! Sounds too good to be true? It may not be that utopist if you use them as a strategic tool. Let's dig into what makes TODOs useful.

The problems with TODOs

Those which aren't actionable

Have you ever had to work with a codebase sprinkled with vague TODOs that have been here for years, written by developers who are long gone now? How frustrating it is to come across such code:

const json = R.pickALL([
  "details", // TODO: remove this

Why should you remove this? Why was it not removed already? Are there side-effects you need to consider?

Mysterious TODOs instill doubt, they slow you down as they force you to consider hypothetical scenarios for which you may not have enough context. They bring confusion.

In general, you move on and leave this TODO as is. You don't really have time to figure this out, but you also don't feel like you can remove it: maybe it was here for a good reason? Again, you don't have time to investigate and justify why this comment can be removed.

A never-ending, invisible backlog

Large codebases tend to be bloated with such confusing comments. Do a quick search in your codebase: how many TODOs do you have? In the back-end I'm working with, we have 141 at the moment—which is OK, I've seen worse.

Technically, each one of these // TODO is a thing that you need… to do. These are 141 issues that have been captured outside of our regular issue tracker. They go completely unnoticed for the Project Manager. But developers see them.

Each TODO adds a little bit of pressure to developers working with the codebase. A mental load you have to carry on as you move through the codebase.

Finally, they distract you from your immediate goal. As you try to focus on the one task at hand, you come across random thoughts that may or may not be related. And now, you have to consider whether or not it makes sense before you can get back to your original task.

What makes TODOs useful

I write TODO comments every day. It helps me focus on the task at hand.

meme I don't always contradict myself

This may sound contradictory. That's because you need to remember there's a distinction between writing code and reading code.

TODOs can be really problematic for developers who have to read them. But at the time of writing, they are really useful:

  1. They get things out of your head. With TODOs you can offload your brain from all the things you have to remember, so you can focus on the task at hand.
  2. They help you get familiar with the codebase. By dumping your thoughts as TODOs comment, you're building comprehension of how the code works and what needs to be done. Interacting with the code by inserting TODOs is very efficient to develop your mental model of the code.
  3. They carry more context than a TODO-list on the side. You can write your TODO next to the code it refers to, where it makes more sense. As you're building your mental model of the code, being able to jump from a TODO to another is very handy.

I've also experienced great TODOs that actually saved me time when I read them. In general, well-written comments can help you remember the context you need to prevent doing mistakes when touching unfamiliar code:

  age: passenger.age,
  // TODO: Seat Selection not supported yet for this scenario.
  passenger_selected_seat: null

If you come across this kind of comment, you can have more context about what needs to be done. You may realize the issue isn't relevant anymore, so you can close it and delete the related TODOs. Or you'll notice that you can't close the issue because these are important TODOs to tackle.

This way, you won't forget any TODO in the codebase as you can simply search for them by the issue number. A simple trick to solve the invisible burden problem!

Great TODOs carry useful context and bring precision to related issues.

I write TODOs all day long!

They help me keep the focus on what I'm doing. Any time I think about something I'll need to do to move on, I dump it in a TODO. Usually, I don't even commit them.

I also progressively get rid of them. As I try to keep PRs small, I may ship some TODO comments in the main branch. When I do so, I make sure to attach the related issue number. That way, I can ensure everything has been addressed before I close the ticket. New TODOs don't stay long in the codebase. They are a handy tool to help me be productive, ship often, and not forget an edge-case.

I am no genius programmer, I have great tricks 🧙‍♂️

What if you already have hundreds of TODOs in your codebase?

TODOs usually start to be a problem when there are many, spread across the codebase. If the previous heuristic will help you progressively recover, you may be looking for a more pragmatic solution to deal with all of these in a reasonable time.

Regardless of the tool you may found, remember that the goal is not to create yet-another-issue-tracker for your team. A really helpful tool should fill the gap between your codebase and the issue tracker you're already using.

If you're using GitHub (my team does), then this tool will meet your needs.

Tickgit for TODO Github

It analyzes your codebase, looking for TODOs comments. It gives you stats around that, which can be handy if you're planning to recover from it and you want to measure progress.

But mostly, it raises all existing TODOs in a way that can be seen by all stakeholders, PM included.

I recommend you spend a 1h timebox per week with the PM until you got through the backlog of existing TODOs. For each TODO, you should:

  • Determine whether it's something that's still relevant, related to an existing issue, or that would be tackled in the next 6 months.

    • If so, create an issue and update the TODO to add the link to the issue
    • If not, remove the TODO from the code

Categorizing each TODO should be quick. If you can't determine what to do with it (e.g. you don't really know how important it might be), then create an issue. If this issue is still here 6 months later, then it's surely not that important after all.

There's also a CLI if you want to integrate that with your existing tools.

This simple habit is a realistic way to recover from a codebase that's bloated with enigmatic // TODO. Little improvements every week might feel futile at first, but they do wonder over time, even on the most gigantic codebases!

Thanks to Nicolas Carlo for this collaboration. If you are interested in reading more of his work, take a look at his article on how to get back to code faster after a meeting., or take a look at his blog page Understand Legacy Code and follow him on Twitter @nicoespeon