#TODOs Never Get To-Done

Jared Carroll ·

Over time all software begins to rot. At first a young codebase is small and lean, it’s a pleasure to work on, a single developer may even be able to understand the entire codebase. However, iteration after iteration time constraints and poorly specified features begin to manifest themselves in the codebase. TODOs are everywhere, tests are pending, waiting on unanswered questions related to their pending functionality, half-baked ideas remain commented out so we won’t “lose” them, new developers join the team and contribute to the rot – the broken window theory is in full swing.

Uncle Bob tells us that our goal as developers is “not just working code but clean code” and that we should “always check in code better than you found it”. As professional developers it’s our responsibility to take the initiative in keeping our software maintainable. Let’s take a closer look at some of the above smells.


# TODO: Update protected instance variables list

# TODO: Validate that the characters are UTF-8. If they aren't,
# you'll get a weird error down the road, but our form handling
# should really prevent that from happening

# TODO: button_to looks at this ... why?

# TODO: Remove these hacks

Your codebase is not the place for TODOs. The problem with TODOs is that most developers quickly just glance over them, ignoring them because they didn’t write them. Sadly version control often reveals that they’ve been around for quite some time. Is a TODO even still applicable? Was it fixed and someone forgot to remove the TODO? Is there an existing ticket for this TODO?

Move this “necessary” work where it belongs; to your project management/bug tracking tool with the rest of your codebase’s features and bugs. Do the same to FIXMEs, REFACTORs and WTFs.

Pending Tests

describe '.recent' do
  it 'does NOT find admins'

describe '#save' do
  it 'normalizes its website URL' do

Every testing tool seems to have the ability to mark a test as pending; RSpec has #pending, Cucumber has @wip, JUnit and NUnit have ignore. These pending tests appear in the output on every test run. Despite this very visible characteristic developers often will let pending tests live on for ages. Test run after test run the same pending tests are output and over time the team learns to live with it.

Pending tests are useful as a kind of TODO list of tests during TDD. And it’s fine to use them while developing a feature in a topic branch but make sure they’re no longer pending when you finish your work and its time to merge it into an integration branch (e.g. master, develop/next, staging, production, etc).

Commented Out Code

def save
  kick = super_save
  # message = { ball => self }.to_json
  # $kicks_machine.publish message

Comments in code are distracting, commented out code is even more distracting. It often sits there for days, weeks, or even months; each passing developer scared to delete it. Even though we’re using version control and we know that every modification made to every file is completely recoverable for some reason we still don’t delete commented out code.

What to Do

Use the following steps to deal with all this cruft, your codebase will thank you for it.

  1. git blame file
  2. Contact the author to discover why
  3. Create a story/ticket for the TODO/pending test/commented out code
  4. Delete the code

Clean Code

Taking the initiative and raising the standard of code cleanliness is contagious. Suddenly your teammates will notice the increase in quality and start to adopt the same techniques. Slowly the codebase improves; there are less distractions, it appears more focused, it starts to look like it was written by developers who take pride in their work. Addressing the above smells are the kind of small changes that individually might not seem like much but over time can make a big difference.