“Wow, you have an insane number of TODOs in the codebase.” — many teammates
There are lots of different opinions engineers have about TODOs and how they fit into the development workflow. I personally think they belong (and add value) in production-ready software:
TODOs help you avoid premature optimization
Imagine you’re an engineer building a feature. Implementation-wise, there’s intimidating number of possibilities, from the high-level approach all the way down to naming classes and methods. You’re not sure where to start, and you can’t afford to go down the rabbit hole building the perfect solution — we’re demoing this next week!
What’s the right approach? It depends on the nature of the business and the team’s tolerance for scrappiness. In rare cases, there’s a solution that takes 20% of the originally scoped time and delivers 80% of the goal. In pursuit of that, I like to (1) implement the most barebones idea to start with, (2) list followups as TODOs, and (3) prioritize the most important ones now, then leave the others in the codebase. Some examples of future work that could be noted as a TODO:
Potential performance improvements — e.g. an opportunity to batch multiple requests into a multiget.
Handling complex corner cases — maybe we show something suboptimal to logged out users.
Simplifying unnecessary complexity or awkward code structure — sometimes the way you wrote it the first time works but is unintuitive, and you think there may be a simpler way to do it.
TODOs are like asynchronous tasks in a web request — the work is not critical enough to block the current request (commit), so they’re deferred to be done later at some point.
Do the low-return, high-investment stuff later
If you’re not sure whether to leave something as a TODO, you can ask yourself:
What’s the return on implementing it? Conversely, what’s the cost of not including this in our current commit? What implications does this decision have on the company mission and on the codebase? Will it be easier or more difficult to do this work later?
How easy is it to implement? Is it hard/complex? Does it require additional investigation?
Some examples:
Validating a web request
Not doing this could introduce large security risks for the company
-> high return, low-medium investment -> Do now
Optimizing performance for a flow 0.05% of users run into
-> low return, medium investment -> Do later
Making a base component less error prone
Although return might be low in the short-term, this pattern might be copy-pasted several times, and fixing issues in all components will be exponentially more difficult
-> high return, medium investment -> Do now
For high investment work with low returns, is it ever pragmatic to do? Keep in mind that the above ROI calculation is a human estimation given today’s world, and the world changes over time. Imagine a small TODO: parallelize
comment in an early 2002 AWS codebase — today, that code might run a billion times more frequently and be a >$100M optimization.
Revisit your TODOs
Build a habit to grep your TODOs. Weeks, months or years later, you have a better idea of how the code is being run and the business impact of the feature. Even if you don’t have time to solve these immediately, at least it refreshes your memory.
Some may be larger issues now than you originally anticipated, and are worth their own Asana tasks or JIRA tickets.
Maybe you’ll magically come up with an elegant solution while washing the dishes, or on a walk later that week.
In my experience, often the original code is fine as-is. Maybe you can even clean up the TODO if it’s no longer useful.
This process can also be fun! For me it’s a refreshing way to take a break from other work, and solve problems in small batches with high context.
Dan Abramov on TODOs:
TODOs in the Linux codebase (source):
And Kubernetes (source):