Dealing with obsolete code

… or how to know when to drop unused stuff?


Is your codebase polluted with obsolete code snippets? Do you have feature toggles that never get dropped? Would you like software to tell you automatically when obsolete code can be dropped? The problem with dealing with obsolete code is that we don’t really know when it becomes obsolete, because complex conditions may apply (for example, if new feature is enabled on all production nodes and survived peak load, then old feature is considered obsolete and can be confidently removed). In this article I’ll share some ideas how to know when to remove obsolete code and feature toggles without pasting your monitor with sticky notes.

Traditional approaches for dealing with obsolete code


I will do nothing with it.

For many Agile teams, especially those working in Continuous Delivery manner, “done” means released to end-user. However, in order to release something safely without breaking old functionality, old functionality should be kept in code for not-well-known time. New features come, old features stay. No one cares, this is “just one feature toggle”, isn't it? One of thousand and one.

I will pin sticky note on the monitor.

Not the best strategy given sticky notes multiply, magically disappear and get recycled by office cleaner.

I will schedule a user story.

I believe that tiny technical tasks must be handled in background. Do you schedule code refactoring, unit test development and deployments in Backlog? Hope you don’t.

I will leave TODO in the code.

TODOs are virtual representations of sticky notes. Same problems, different angle. Do you believe someone is going to clean up your TODOs if you head up to another project?

I will review all toggles once in a while.

Sounds similar to “I will refactor all code once in a while”. Idea is pretty cool except no one does it and it lacks automation.

Smarter approach


I like this approach because it’s simple to both implement and understand. Let’s see how it works step by step.

1. Mark legacy code snippet

We begin with marking legacy or “droppable” code snippets with a special comment that follows uniform pattern, and give the code snippet a name. In the Java example below we mark two snippets: doStuffInAnOldWay() method should be dropped when new_do_stuff_toggled condition is satisfied (feature is toggled on production) and sendMarketingCampaign() should be dropped when marketing_campaign_finished condition is satisfied (let’s say, day after 8th of March).

https://gist.github.com/eduardsi/4e94c52a1cc605ef6c8f

2. Add drop conditions to a special script

We have to create a script that will be responsible for drop condition evaluation. Let’s use Groovy for scripting. Create DropConditions.groovy with the following content:

https://gist.github.com/eduardsi/a299280fbaccf1f786bb

it’s actually nothing more than a map with code snippet names as keys and special closures as values, which return true only if drop condition is satisfied. Let’s implement cfg and timeCame drop condition methods.

3. Implement drop condition methods

We have to do it only once. timeCame() is pretty simple:

https://gist.github.com/eduardsi/336c08ce65b8ab6597f0

… and cfg():

https://gist.github.com/eduardsi/dd27bae4bcf5de423422

cfg() fetches production configuration for the given key. Hopefully your configuration management system has sexy HTTP API and you can easily get read-only access to it. Keeping configuration in property / YAML bundles? Ok, just write a small production service that exposes configuration via HTTP or change application so it exposed it. Using ZooKeeper? Ok, things are getting more complex and you may love using Curator to simplify client implementation.

4. Implement code traversing

The following code snippet goes through all .java source files and evaluates drop conditions:

https://gist.github.com/eduardsi/1b1f3753198d91bb2420

5. Put it all together

Merge aforementioned snippets into single groovy script and keep it in the root project directory under version control.

You’ll end up with something like this:

https://gist.github.com/eduardsi/e7fe8f364bbb3bebfe97

When evaluated, the script will print out drop conditions that are satisfied and errors associated with drop condition evaluation (if there are such).

6. Add Dashboard to Jenkins

With help of Groovy Plugin and Groovy Postbuild Plugin we can achieve something like this:


Just after every build we can perform code analysis and show build warning icon near a build job if there are “droppable” code snippets. On the build job page we can see a list of droppable code snippets and issues that occurred during code evaluation (e.g. when script couldn't fetch remote value).

Simple implementation:

  1. Execute Groovy script during build (I usually do it in a separate job that shares workspace with the upstream job with Clone Workspace SCM Plugin)
  2. Add the following script to the Groovy Postbuild step:
https://gist.github.com/eduardsi/0bec8d8ccd3e584f6f5b

Voila!

More

When working in multi-node environment you can drop code only if application is deployed to all nodes. Things are getting worse when there is no centralised configuration management solution in use. In order to handle this use case, you must make production node inventory available either through node discovery application (like ZooKeeper or Consul) or through generally accessible inventory file (e.g. yaml). Then just iterate though nodes and ask them for production configuration. So simple.

Imagine we have to drop code if particular state in DB changed? Naively Groovy script can access production DB, but that’s not how things should be done. Let application access production DB and expose information via HTTP (a-la foo_column_present=false). Then ask for value in the script.

Conclusion

That’s it. Now you can run your own drop conditions by leveraging the power of Groovy and keep the campground clean.

Enjoy!