title: ‘Prioritizing technical debt vs. feature development in your Django app (This Old Pony #55)’ layout: newsletter published: true date: ‘2018-07-18T10:45:00.000Z’
We’re back to our mini-series this week on prioritization when working with existing Django apps. This week, we’re going to tackle the most fun of all the issues working with existing applications (of any kind): prioritizing paying down technical debt versus tackling feature development.
In no small part because its inherent in the name, we’re going to use some financial metaphors throughout. There are probably as many definitions of technical debt as there explanations of monads (technical debt is not a burrito[x0). And there is value is pointing out different explanations, but I’d like to start with this:
Technical debt is the manifested opportunity cost of technical decisions.
Some previous developer or developer team was faced with a decision to make. They had two options in front of them and they chose one. The cost of not choosing the other is now what you have to deal with. This isn’t necessarily bad programming. A lot of people confuse technical debt with bad programming, and it’s rare that bad programming is the cause of technical debt. By bad programming, think infinite loops. The kind of code you look at and think, “A team of drunken monkeys must have written this.”
Usually it’s because there was some sort of change to business strategy, or features, or some sort of use that the previous team couldn’t anticipate. They made the best decision with the information available at the time, and everything changed. The world around the application changed and now there are structural issues that you have to deal with. This is technical debt.
The most obvious and common cost of technical debt is development time. Technical debt often means older, incompatible software versions. It might mean missing tests, bad tests, or slow tests. These all represent friction, to varying degrees.
Let’s take a “bad” architectural decision, for example, perhaps involving excessive coupling of Django models across apps without a clear hierarchy. This now makes it harder to isolate and reuse functionality. And aside from making it more difficult to make changes to the existing code, it may make it significantly more challenging to add new functionality. You think of a good way to add a new feature and then reality hits - you’re going to have to break this up over 4 different modules and one of them isn’t tested well and… if only someone had fixed this earlier.
Technical debt can also result from a good decision, in the context of the time and requirements the decision was made, which now no longer fits the app but has been worked around instead of updated.
Let’s say you’ve got $10,000 worth of credit card debt at a uniform 9% APR, $10,000 in spare cash, and an investment opportunity that you expect to return 15% over the next year, and no other options for how to use that cash. Which is the best choice?
Now let’s say you have a business with $10,000 in debt obligations at 9% APR, $10,000 in spare cash, and an opportunity to invest back in your business in a way that return 15% over the next year, again with no other options for how to use that cash. Is it still so clear cut what you’d do?
In the context of personal finance you should really pay off that debt. But business finance is a little different from personal finance, and sometimes what makes sense in one arena makes less sense in the other. In business, debt or leverage is fuel for growth. It’s the oxygen which modern economies need to thrive. It makes sense to take on future obligations when doing so allows for greater returns.
And this is where the analogy for technical debt shines. It is a debt, a future obligation, and like a financial debt it gets harder to pay down the longer debt servicing is put off. And at the same time, when there are pressing alternatives, it makes sense to weigh how much of the debt should be paid down versus how much cash is applied to other investments (or even, dare I say, operations).
The obvious scenario is where a feature will get a product or business out of or past a trough, especially if that trough leads to failure. In such a scenario if developing a new feature or extending an existing one gets the business past the trough then it makes little sense to spend weeks refactoring and rearchitecting internal libraries.
It might sound a little strange to hear someone tell you not to pay down technical debt. Let’s be clear, I’m not telling you not to pay it down! But what I am suggesting is that not all technical debt has the same cost, and it’s not sometimes worth putting off a bit more the effort to pay it down. It depends heavily on the details of the code and the situation of course, and I don’t have a simple checklist to share (although that’s not a bad idea).
One of the reasons you don’t hear much about how not to pay down technical debt is that just getting people aware of it in the first place tends to be a much greater problem. The problem most development teams seem to have is not weighing how much technical debt to service but convincing anyone with control of product or budget of the costs of not servicing technical debt.
A healthy business uses debt judicially for strategic growth yet is easily swamped by debts allowed to compound.
In your debt,
 This is basically a meme in FP https://blog.plover.com/prog/burritos.html
 The rationale really isn’t pertinent for this discussion, but if you’re interested in an explanation just hit “Reply”