How should we decide what to work on? Answering that question is the key to all software development processes. In my experience a lot of programmers, developers, and engineers are bad at answering it.
This is the elephant in every room; at least in every room containing software developers. If there is software running somewhere, there is technical debt somewhere. At least, something some developer would identify as technical debt. And, developers love to address technical debt. Or, rather, they love to talk about addressing technical debt.
I like talking about technical debt. I like addressing technical debt. But, I and many others enjoy discussing the problem most of all. It’s the easiest way to feel high minded and like we are experts at something. We all want to be pundits. And, nothing inspires punditry like the subject of technical debt among software developers.
This is because it’s easy to talk about. It’s also easy to theorize about. But, it’s hard to fix while also delivering value. Take a moment to run down the implications of that last statement.
Fixing Technical Debt Rarely Delivers Value On Its Own
Technical debt implies the existence of something that already works. And, something that already works has already delivered value. So, how do we change something’s implementation in a way that delivers new value?
Sure, the new implementation may be more performant. But, was that needed by customers right now? Sure, it may be easier to maintain or extend. But, do you need to extend it right now? Sure, it may let you use some shiny new tech. But, will the customer even know or care?
The answers to those questions are too often a definitive “no.” So, why do we need to fix that technical debt right now? The answer is we often do not need to do any of the work we imagine. That is because we both misunderstand technical debt and value. Technical debt is only real if it costs us something. If there is no cost, then who do we owe something to again?
Debt is something owed; and owing something to our egos does not count. Technical debt arises at the intersection of two conditions. First, our code must need active development. Second, changes must have become more difficult to make. Without both those conditions met the debt is not real. This can arise because of pure technical necessity, such as a need to update dependencies. But, unless those updates are necessary they do not qualify as technical debt either. The key is the presence of a necessary cost to pay to continue work.
I love Test-Driven Development. The Red-Green-Refactor cycle brings me great joy. And, refactoring is about keeping things working. Refactoring never involves changing behavior of code, only implementation. It’s about keeping the what and why of a unit of code intact while changing the how. Refactoring is where we discover better abstractions, or unpack ones that have lost their value.
So, if you are working in proximity to code that needs to change to make your work easier: refactor it. That is part of what refactoring is for. It’s called Preparatory Refactoring and is the heart of one of my favorite Kent Beck quotes:
for each desired change, make the change easy (warning: this may be hard), then make the easy change
— Kent Beck (@KentBeck) September 25, 2012
Too often developers forget to refactor as they go in favor of imagining one day they’ll get to do it right. And, too often, doing it right means doing it over. That’s not working smart, that’s working hard. And, it is straight up duplication of labor. That kind of approach has negative value.
Instead, we should make improvements in the course of doing things that we know deliver value. But, that begs the question: what do we mean by the term value?
In the realm of software the only thing that has value is what the customer finds valuable. Technical choices only have value to the extent that they produce value for the customer. That means value is subjective.
That is one of the factors that can tend to trip up programmers. We like to believe we are rational. That is not true. We give in to our preferences, biases, and emotions readily because we are human beings. We bring those biases, preferences, and emotional attachments into how we assess value. But, our assessment is nowhere near as important as the people who actually use our software. Our opinions, preferences, and the like are mostly irrelevant. But, we tend to inject them anyways. And, when we do this we lead ourselves away from understanding what has value.
Our subjective assessments lead us to rank our wants ahead of the customers needs. This can happen when we dismiss a user experience by saying things like: “I wouldn’t want to do it that way.” That sounds like we’re putting ourselves in the customer’s position. In reality we are disguising our biases, and preferences as if it were user feedback.
Unless a customer deems something to have value, it has none. If the customer needs something faster, than the value is clear. If the customer needs a revised interface, then the value is clear. If the customer needs anything, then the value is clear. But, too often we assume that because we want something it has value. We should treat anything not in service of the customer as suspect.
Find ways to address technical debt and technical wants in the flow of normal feature work. Deliver value to yourself and the customer in tandem. That can produce a virtuous cycle and keep you from doing unnecessary work. But, that’s how I like to approach things. What do you think?