IEEE Software - The Pragmatic Designer: Garbage Collect Your Technical Debt
This column was published in IEEE Software, The Pragmatic Designer column, September-October 2021, Vol 38, number 5.
ABSTRACT: The iterative process that a team follows is a bit like a garbage collection algorithm, and we can compare software development processes like we can any algorithm. A process can help developers do two things: clean up tech debt after it exists, or avoid creating it. When an iterative process does neither, tech debt buildup will lead to bankruptcy, so it is only suitable for projects with a short lifespan. A process that does both has the best chance at minimizing tech debt over a long lifespan. In particular, focusing on the system’s design will keep tech debt low.
There is a kind of design distortion that happens when a team chooses to build iteratively instead of looking at all the requirements at once. Ward Cunningham coined the term technical debt to describe those design distortions. (Today, people use the term tech debt to describe a wide range of problems, including bad code written by novices, and known-bad designs used in desperation to meet a deadline. Throughout this essay, we are talking about the original, narrower kind of tech debt, also called ur-technical debt .) By understanding the causes of tech debt and connecting them back to a team’s actions (or inactions), it’s possible to minimize the buildup of tech debt and keep a system healthy, indefinitely. The way to minimize tech debt is to view a software development process as an algorithm, consider several algorithms, and choose the right one for the circumstances.
However, most developers don’t think about their process as an algorithm, so let’s ease into the idea by looking at garbage collection algorithms. Watching tech debt build up on a project is a bit like watching a program allocate memory.
Running a program creates garbage, which is memory that’s been allocated but is unused. Garbage creation is unavoidable, so we must occasionally pause to collect garbage. It’s nicer when those pauses are predictable and short. There are various garbage collection algorithms that have different properties.
And here’s a description of tech debt, using the same phrasing:
Running a timeboxed iteration creates tech debt, which is working code with an obsolete design. Creating tech debt is unavoidable, so we must occasionally pause to refactor the code. It’s nicer when those pauses are predictable and short. There are various iterative software development processes that have different properties.
Consider this: a team’s software development process is an algorithm, run by the team itself, that generates and cleans up a kind of garbage that we call tech debt. We know how to analyze algorithms, so let’s analyze a team’s process just like any other algorithm.
Software development processes control tech debt using two techniques. The first technique is cleaning up existing tech debt. Most teams already do this by refactoring. The second is avoiding the creation of tech debt. This is less common but more interesting. Let’s look at each in turn.
Technique: Tech Debt Cleanup
You can control tech debt by cleaning it up after it exists. Often, a team “bolts on” a feature without regard to the existing design, identifies tech debt, and only then refactors to clean it up. Sometimes the cleanup happens immediately, but it could be much later.
Small problems can be refactored in minutes, but bigger problems can take days, weeks, or months to clean up. When developers take a break from writing features to fix tech debt, that’s like a garbage collector pausing to clean up garbage. Spending time on refactoring means less time for new features. The bigger the problem, the longer it takes to refactor.
Because it requires stealing time from feature building, teams can find themselves under pressure to do less refactoring, especially large refactorings. As a result, they clean up the small problems but delay cleaning up big problems, such as the system’s architecture.2 Postponing a small cleanup can transform it into a big cleanup because, over time, code builds up around the problem, and it too must be refactored.
Technique: Tech Debt Avoidance
You can control tech debt by creating less of it—that is, by avoiding it. Teams do that by considering design alternatives and choosing the one that creates the least tech debt. When asked to add a new feature, a team considers how well the current design can accommodate that feature. If the design is already suitable, they add the feature. But if the design is unsuitable, they update the design first, then add the feature.
Kent Beck summarized it this way: “[F]or each desired change, make the change easy (warning: this may be hard), then make the easy change.”3 The wordplay in Beck’s quote is delightful, but the idea here is not a linguistic trick. Figure 1 shows two possible software development processes to control tech debt. The first allows tech debt to happen, then cleans it up. The second looks for upcoming trouble and avoids it by redesigning before implementing the feature.
- G. Fairbanks, Ur-Technical Debt, IEEE Software, Vol 37 number 4. July-August 2020.
- M. Keeling, Headwinds to Redesign. IEEE Software, Vol 38 number 2, March-April 2021.
- T. Halloran, Fog of Software Design, IEEE Software, Vol 38 number 3,May-June 2021.
- T. Halloran, Development of the StratWar Wargame Software, Practicum Report, DTIC ADA428794, February 2004.
- G. Fairbanks, The Rituals of Iterations and Tests, IEEE Software, Vol 37 number 6. November-December 2020.
- G. Fairbanks, Why Is It Getting Harder to Apply Software Architecture? IEEE Software, July-August 2021, Vol 38, number 4.