Saturday, May 9, 2009

Refactoring Pays Down Technical Debt

One of my first posts on this site mentioned technical debt, a brilliant metaphor for the way that hurrying imperfect code out the door provides short-term gains but long-term costs for new features. Enough technical debt, and your feature development slows to a crawl. One way to pay down technical debt is to refactor code: Refactoring's main role is to adapt a code base to requirements that weren't on the table when the programmers started typing.

I've talked about continual refactoring before, which I do as a general rule in any code I find myself in, but that is usually directionless cleanup. A deliberate refactoring phase is even better. (As long as you don't fall into the "rewrite everything" trap that Netscape found itself in, which caused such a long delay in version 6 that the company essentially handed the entire browser market to Internet Explorer.)

In my iPhone app, I have three "detail" views that, when I first wrote them, had only surface-level similarities.* They had different DAL requirements, used different objects, and so forth. But as I added new functionality to each, such as my ScheduleButton widget and my date picker widget, they began to look more alike than not. I entered a bug for myself to refactor all the common code into one base class.

A week later, I did the work. There was no design requirement driving this: I just knew the code would end up in a better place at the end. I abstracted common functionality into superclass utility methods and used the template method design pattern to put a flow into the base class that used abstract methods ("abstract," because Objective C doesn't support this) to fill in view-specific details.

A week after that, I realized I needed to change the method signature for my date picker delegate methods to support a new feature. That meant going into all the delegates and updating the code. A simple search/replace wouldn't do what I needed.

Before my refactor, that fix would have affected eight methods in four classes. After my refactor, it affected four methods in two classes. I halved the development effort it took to support this new feature. But I also halved the testing effort. Since all my detail views descend from a base class that handles all the date chooser work, I only had to test one of them — plus one unrelated view — to ensure that all three were working. If I need to add a feature across all the detail views, it will take me one-third the time it would have before.

* As always, I should point out that my iPhone app just isn't that big. I'm unlikely to ever have a vast amount of technical debt in it. Still, it illustrates the point in a microcosm. Larger systems have proportionally more complexity and thus more opportunity for technical debt.

2 comments:

  1. "Entered a bug for myself" -- what are you using for bug/task tracking? I'm sure you've said in twitter before. For my personal projects I have a todo list in a text file...

    ReplyDelete
  2. I'm using FogBugz. It's free for single-developer (or even two-developer) shops, and it fits my needs. It has lots of functionality I don't need, but it lets me differentiate features from bugs, set severity, set category, set release targets, and so forth.

    ReplyDelete