Innovation is great.  Discovering new and better ways of doing things is pretty much what we mean by improving a software product.  However, just as a fairy tale princess may need to kiss a lot of frogs before finding her prince, many innovations go through several attempts before paying off.  As a result, we often accumulate a half dozen “better” ways of doing the same thing.  That inconsistency is one of the most common forms of technical debt.  This post is about one tactic for paying down that debt the same way it accumulates – a little at a time, while we’re focused mainly on other things.

Before diving into the solution, let’s be sure we appreciate the full magnitude of the problem.  In a nutshell, the code base contains multiple approaches to many different problems.  This diversity can arise in several ways.  Sometimes, we deliberately introduce a new framework or component to replace an old one.  Sometimes we do so because we encounter a novel requirement that reveals a gap in our prior approach.  Often, a developer who is unfamiliar with an existing approach “reinvents the wheel.”  Once introduced, these divergent solutions have a remarkable tendency to “go viral.”  Each time a similar need arises, developers searching for an example to go-by are likely to follow the first example they find.  Before we know it, these accidental innovations have infected so much of the code base that cleaning them up is doubly difficult.  On one hand, the effort is daunting and the risk is considerable; there’s a LOT of code affected.  On the other, the payoff is open to question; all that code “works.”  Still, we’ve seen where that road leads.  As diverse approaches to several problems proliferate, they infect more and more code (some of it affected in multiple ways).  Confusion increases geometrically, driving the code base into a death spiral.

Why is this so hard?  Couldn’t we just* deprecate all the obsolete stuff and then clean up the warnings?  As they** say, “for every problem, there’s a solution which is simple, obvious, and wrong.”  The first challenge here is to gain consensus as to which approach should be the standard.  This probably won’t be easy.  After all, we had reasons for introducing most of those approaches, and many of those reasons still apply.  When and if we achieve that consensus, we immediately discover a more pragmatic obstacle: As soon as we deprecate all those divergent approaches, we are buried in an avalanche of deprecation warnings – references to that stuff is everywhere.  Not only do we have no idea where to start, but it’s easy for the noise of these “old news” warnings to mask new problems which might be more serious.

Well, they also say, “most beautiful ideas have ugly parents.”***  The idea of deprecating disfavored approaches so others won’t further proliferate them seems like a good one.  We just need a way to do it without provoking all the side effects.  The solution comes in two parts, one social, the other technical.  Socially, although it’s often hard to gain consensus on a single best approach to a class of problems, it’s often much easier to agree on one or more that just aren’t cutting it.  Having two approaches is quite an improvement over having four or five.  By focusing on what to clean up rather than picking one true way, deprecation helps us make the progress we can and sets the stage for further improvements.

From a technical perspective, what we’re after is (a) to guide future development (including maintenance) away from disfavored approaches and toward better alternatives, and (b) to plant reminders where the disfavored approaches are used, so the next time we’re “in the neighborhood” we can clean them up.  In other words, we’d like to aggressively discourage any new usage, and we’d gently encourage cleaning up existing usage.

Fortunately, we can have it both ways.  At least in Java (and I’m looking into JavaScript and Ruby) the @deprecated annotation has a helpful companion, @suppressWarnings(”deprecated”).  We can discourage new usage by applying @deprecated to things we’d like to stop using, and we can use @suppressWarnings(”deprecated”) to both “grandfather” and call attention to existing usage.  Especially with modern IDEs, suppressing these warnings is easy at best, and only moderately burdensome at worst.  Still, for reasons of both thoroughness and ease, the suppression piece is best accomplished with a script written in Perl or another language that’s good with regular expressions and directory traversals.  Upcoming posts will explore such scripts for Java, Ruby, and JavaScript.

* “Couldn’t we just…” seemed to be the life-motto of a former manager.  If these words don’t fill you with fear and loathing, they should.

** This quote is variously attributed to H.L. Menken, Mark Twain, and Albert Einstein.

*** I’ve got no idea on this one.