« blog

Tech Debt and Taxes

In Reframing Tech Debt, Leemay Nassery suggests framing technical housekeeping positively — as building “tech wealth” — to convince business and product stakeholders it’s worthwhile.

If I were one of those stakeholders, a self-professed customer-obsessive, I’d balk (rightfully!) at the idea of a secondary system of value independent from the user experience. “What does ‘tech wealth’ mean for a customer?” Fair question!

I think a negative framing is more useful here, but Nassery’s suggestion that, say, refactoring a program yields a resource to reinvest gets at the issue with calling it “debt.” Technical “debt” sounds like a fixed cost that can be deferred; more accurately, we should say debt exacts an ongoing tax from go-to-market teams. The costs of quick development may be hidden, but they aren’t externalized.

If you make implementation shortcuts in the name of product velocity, but you don’t have the discipline to clean them up, those shortcuts will eventually be counterproductive to the speed-of-delivery you hoped to prioritize. Changes to a codebase are more often integrative than strictly additive: they have to contend with the code that’s already there. Writing sloppy code today means writing code slowly tomorrow.

In each of these cases, tech debt’s the common enemy of the product stakeholder and even the most navel-gazing, customer-indifferent engineer. While the underlying debt isn’t addressed, the stakeholder pays a tax — as a delay, or as systematic under-verification that’ll eventually yield bugs.

This dynamic is particular to API and product debt — unnecessary complexity in how parts of the system interact with each other (interface and data model design, documentation, and testing practices) or the space of customer states (feature flags and billing). These forms of debt tax new projects in proportion to those projects’ complexity; big projects and radical changes in product behavior are proportionally punished. Of course, not all “tech debt” should be prioritized this way; poor database performance might usually be considered debt, but it’s less likely than an unexpressive API to derail your next feature.

The usual refrain — including in Nassery’s article — is to preallocate blocks of time for building tech wealth. The “tax” translation from tech into customer impact should let a team prioritize specific tech debt initiatives, according to the same criteria as features.2

Unsurprisingly, it falls to engineers and their managers to use this framing device effectively.

Business and product stakeholders can take some proactive action.

The issue with “tech debt” and “tech wealth” is that they underemphasize software’s impact on a company’s agility, its ongoing ability to adapt to serve its users’ needs. Without recourse to customer value, advocating against technical debt means advocating for a separate and competing system of value, something business and product stakeholders are right to treat with skepticism.

Emphasis on the productivity impact makes technical debt expressible in the primary system of value, where it’s comparable against — and can be prioritized over — any other company initiative.

Process Plants: A Handbook for Inherently Safer Design (Kletz 1998) reminded me of this essay. A process plant designer can intervene to prevent hazards (e.g. a “snakepit” plant layout), but investors don’t value hazard mitigation for its own sake. Quoting Kirkland,

This cannot all be the fault of the “money men” and their failure to understand us. It is often much more our inability to express ourselves in a language they can understand.3

I’m more optimistic than Kirkland. I think the “money” stakeholders do understand hazards, but that they’re better-equipped to predict and prioritize money. To that end, Kletz offers napkin math:

Remember that if it costs $1 to fix a problem at the conceptual stage, it will cost

Software could use some equivalent with costs in various stages of planning, development, and deployment. “Hazard” may be a better analogy than “tax” for describing the running cost of technical debt.

  1. Take the time to keep your feature flags mutually independent, even if that means running migrations to split or merge flags; make invalid states unrepresentable. If you don’t, each new flag effectively doubles the user interface surface you design and test.↩︎

  2. Development acceleration should have a double-integral relationship to the value delivered by new development, but expressing this accurately would require some complex discounting.

    Imagine you can divide your fixed engineering time in a quarter between tdebt and tfeat. Customer value delivered looks something like (v + tdebt) ⋅ tfeat, where v is your initial product velocity. As tfeat approaches zero, the team spends all of its time preparing to deliver value… but none of its time actually doing so.

    This model is short several coefficients, but hopefully you get the idea.↩︎

  3. Kirkland, C. J. The Channel Tunnel: Engineering under Private Finance — Innovation or Frustration (London: Fellowship of Engineering, 1989). Quoted in Kletz, page 157.↩︎

  4. Kletz, Trevor. Process plants: A handbook for inherently safer design. CRC Press, 2010. Page 165. For specific examples of how “chang[ing] early” saves money, see page 202.↩︎