18 March 2009

Ward Cunningham's Debt Metaphor Isn't a Metaphor

Last month, Ward Cunningham posted an excellent video on YouTube regarding refactoring and "debt". If you haven't seen it, I have it for you right here...

But is it simply a metaphor, or is he describing a real fiscal debt associated with software development activities?

The form of technical debt that Ward is referring to is noted by developers as poor, brittle design. Since a poor design is one that's difficult to change or maintain, it will take developers longer to add features, and their changes will more likely result in defects. They will tip-toe slowly and cautiously within (or around) untested code, and even the best developer will inadvertently introduce the occasional defect.

Isn't developer time expensive? Of course. So, in effect, Design Debt is real debt. I wouldn't suggest you create a liability account in QuickBooks: It's difficult to measure the actual dollar value, and it manifests itself in subtle and even contradictory ways. For example, remember the old story about the manager who proclaimed, "if only my developers could type faster!"? That's akin to saying "if only I could use my credit card faster!"

Design Debt feeds into another form of debt which is easier for the business to see: Quality Debt. This can be quantified as the number and severity of defects experienced by users. This metric has a fairly direct relationship with the financial impact of lost sales, time spent on support calls, developer time spent debugging the product, and on and on (and on).

Yet how many teams just assume that Quality Debt is "the nature of software development"? Well, doctors were using rattles and snake venom 50 years into the profession of medicine. Aren't you glad they're not still clinging to those archaic practices?

Unfortunately, what we usually try to do may just make it worse...

The functionality grows, the complexity grows, and it all has to be tested. We hire more testers, or give ourselves longer for the test "phase" of an agile iteration. (Yes, this "mini-waterfall" approach has been noted on many "agile-ish" teams. If waterfalls are bad, why are more of them better???)

And the stack of manual test-cases grows and grows, until the poor test team is working late at night to get them all tested: Point-click-type-point-click-type... and even our infallible testers make mistakes!

Or, the team runs only the "critical" tests each iteration, and saves the whole suite for the pre-release shake-down cruise. But which tests--which product features--are not critical? And waiting until the end...well, that sounds a lot like waterfall again, and also sounds like a Home Equity Loan, if you get my meaning.

The third form of debt is Testing Debt. The anecdotal measurement of Testing Debt is the height of the stack of printed manual test-cases. A better measure would be the time it takes to run the entire regression suite. How long after a change does it take the team to know that they haven't broken anything they've already built?
On the University of Michigan Transplant Center's two-year "OTIS2" rewrite of their aging Organ Transplant Information System; and after weekly reminders that "a mistake could kill a patient"; the answer was 15 minutes.

My goal isn't to make you feel bad in comparison, dear reader, but to point out what's truly possible without any technical debt.
Is Testing Debt real financial debt? Well, there's testers' time and salary. Also, Testing Debt again feeds into Quality Debt. If you don't assure that there are no bugs, your customer will find them for you.

But It's 0% APR, Right?

There is interest. You know what you get when you add features to buggy code? Yeah, more buggy features.

Thanks! You've Ruined My Day. Now What?

First, in the immortal words of Douglas Adams: Don't panic. How does anyone get out of debt?

1. Make regular payments.
2. Stop accruing more debt.

Examples of how agile teams can pay down technical debt:

Design Debt: When tasking and estimating stories, the team should consider the refactorings necessary to get any new story designed correctly. Those refactorings become explicit tasks.

Quality Debt: Set aside time within each iteration to explore and fix high-priority defects, or create a story for a group of related bugs. On a well-running agile project, each reported defect becomes a story of its own.

Testing Debt: Set aside time within each iteration for testers to automate those "critical" tests, or a particularly onerous set of tests. If you're not ready for that, create a story for the team (testers and developers working together) to explore automation tools, including record/playback tools as well as ATDD tools such as Fit. Yes, you're going to have to slow down the introduction of additional stories/features.

Ways to avoid new debt:

Design Debt: Developers, adopt Test-Driven Development (TDD) and refactor in tiny increments as you identify seemingly insignificant trends towards poor design. Whenever a task or story requires a new variation in your design, first refactor the appropriate piece of the design into one that follows the Open-Closed Principle for that variation. (That's as concise as I can be without going into gory detail here. Explaining this technique requires lots of UML, at least, and I'd probably rather use a video medium.)

Quality Debt: Pin down desired behavior immediately with fast, automated tests. Adopt TDD and ATDD (Acceptance-Test TDD) practices to keep away any new defects. Avoid feeding Quality Debt from the other two forms of debt.

Testing Debt: Stop writing manual test cases, perhaps today. As soon as practical, build acceptance tests for stories using something allowing test-first automation such as Fit, Robot Framework, or Cucumber. It's also up to the team to find ways to make those tests run fast.