> although the phrase technical debt caries a lot of emotional weight, it also brings a lot of baggage from all the times management has heard the phrase from someone that didn't understand the bigger picture.
I think this is an excellent point, but we still need a shorthand way to talk about it.
> They'd say instead something like "I was thinking that if we changed X we would get value Y."
The problem I have with this is it's very hard to make a compelling argument, and without one, nothing gets done. A lot of the time technical debt is tantamount to a slow death by a thousand cuts, and "value Y" is more like "save some unknown but non-zero amount of time in the future".
One example is when you have (tens of) thousands of lines of code that don't have unit tests, have high coupling, and no or outdated documentation. Maybe there's some known bugs, but they're of low importance (in other words: fixing them is low value to the business). That code works today, and if not modified it'll continue to work. If it does eventually get modified, then there are no guarantees: maybe the modifications won't break anything, or maybe they'll cause dozens of new bugs.
If we refactor today and add lots of tests, it'll probably mean that future modification causes fewer bugs (saving time/money), but we actually can't guarantee anything -- aside from the refactor will cost a bunch of time, now.
> a huge amount of technical debt is just code that a more senior engineer would refactor as they went
In a case like I described above you can sometimes refactor as you go but it's been my experience this is often a deep rabbit hole. Sometimes your refactor requires touching a dozen layers and related bits and before you know it it can easily be several orders of magnitude more work and more risk vs the "quick fix".
Another thing I often run into that we describe as "technical debt" is about foundational designs that are (currently) wrong. This includes things like database schema and core structure of the application. I say "currently" because some where not clearly wrong at the time they were made, but it had become clear since then and yet more stuff was still built on top.
As an example, I'm working on an application that was built to run on a single instance but we'd like to be able to scale horizontally. One of the problems is the application uses what is effectively an in-process cache for a lot of the database objects. The objects are not pure DTO-style objects and so the cache can't just be moved to external (Redis or something). The schema is not that well designed, so entirely removing the cache would almost certainly kill performance, and might mean refactoring half the code anyway. A layer up, several parts of the app (including UI forms and some background processing) are built assuming they have a consistent view of things, and ignoring these issues causes all kinds of strange consistency problems as data is silently overwritten/changed.
That type of problem is not at all fixable by "refactoring as we go". In some sense I'd love to do a totally clean rewrite, but that just isn't going to happen largely because the business has no appetite for that. In the past I've done the massive rewrite thing before, and going a couple years without delivering anything from it is crazy stressful and no one is happy.
Instead, we're working on rewriting major (but approachable) chunks of this one at a time, while retaining compatibility with other parts of the system and still trying to find things that provide customer/business value as part of it (either fixing long-standing bugs or introducing new features). It's hard, and overall is more work than just rewriting from scratch, but means we can deliver value as we go. Maybe this is what you mean by "refactor as we go" (or maybe that's just how I should describe what we're doing to higher-up), but to me we're basically starting from zero and at best, pulling small chunks of the old code in, so it definitely doesn't feel like "refactor".
(I know this post is explicitly about not calling this "technical debt" but I can't help but think this approach is analogous to a payment plan. :) )
I think this is an excellent point, but we still need a shorthand way to talk about it.
> They'd say instead something like "I was thinking that if we changed X we would get value Y."
The problem I have with this is it's very hard to make a compelling argument, and without one, nothing gets done. A lot of the time technical debt is tantamount to a slow death by a thousand cuts, and "value Y" is more like "save some unknown but non-zero amount of time in the future".
One example is when you have (tens of) thousands of lines of code that don't have unit tests, have high coupling, and no or outdated documentation. Maybe there's some known bugs, but they're of low importance (in other words: fixing them is low value to the business). That code works today, and if not modified it'll continue to work. If it does eventually get modified, then there are no guarantees: maybe the modifications won't break anything, or maybe they'll cause dozens of new bugs.
If we refactor today and add lots of tests, it'll probably mean that future modification causes fewer bugs (saving time/money), but we actually can't guarantee anything -- aside from the refactor will cost a bunch of time, now.
> a huge amount of technical debt is just code that a more senior engineer would refactor as they went
In a case like I described above you can sometimes refactor as you go but it's been my experience this is often a deep rabbit hole. Sometimes your refactor requires touching a dozen layers and related bits and before you know it it can easily be several orders of magnitude more work and more risk vs the "quick fix".
Another thing I often run into that we describe as "technical debt" is about foundational designs that are (currently) wrong. This includes things like database schema and core structure of the application. I say "currently" because some where not clearly wrong at the time they were made, but it had become clear since then and yet more stuff was still built on top.
As an example, I'm working on an application that was built to run on a single instance but we'd like to be able to scale horizontally. One of the problems is the application uses what is effectively an in-process cache for a lot of the database objects. The objects are not pure DTO-style objects and so the cache can't just be moved to external (Redis or something). The schema is not that well designed, so entirely removing the cache would almost certainly kill performance, and might mean refactoring half the code anyway. A layer up, several parts of the app (including UI forms and some background processing) are built assuming they have a consistent view of things, and ignoring these issues causes all kinds of strange consistency problems as data is silently overwritten/changed.
That type of problem is not at all fixable by "refactoring as we go". In some sense I'd love to do a totally clean rewrite, but that just isn't going to happen largely because the business has no appetite for that. In the past I've done the massive rewrite thing before, and going a couple years without delivering anything from it is crazy stressful and no one is happy.
Instead, we're working on rewriting major (but approachable) chunks of this one at a time, while retaining compatibility with other parts of the system and still trying to find things that provide customer/business value as part of it (either fixing long-standing bugs or introducing new features). It's hard, and overall is more work than just rewriting from scratch, but means we can deliver value as we go. Maybe this is what you mean by "refactor as we go" (or maybe that's just how I should describe what we're doing to higher-up), but to me we're basically starting from zero and at best, pulling small chunks of the old code in, so it definitely doesn't feel like "refactor".
(I know this post is explicitly about not calling this "technical debt" but I can't help but think this approach is analogous to a payment plan. :) )