I often use the term "technical debt" to mean code that isn't ideally structured, or just "bad code." Many other devs do too. The term comes from Ward Cunningham, an OG developer best known for creating the first wiki.
We use the term to refer to many different code problems. Anything we had to do quickly, and not the way we would have liked, we call "technical debt." But what does it mean to do something "too quickly"?
Writing code "too quickly" means we don't have time to understand how it relates to other parts of the project. Whenever we do this, whether under time constraints or another sort of pressure, we are incurring technical debt.
This can have many different symptoms:
* Duplication (unsure if two syntactically similar blocks represent the same concept, and don't have time to work it out)
* Inconsistency (don't have time to look at how similar problems were solved in this codebase)
* Missing tests (don't have time to see how similar code is tested and work out how to adapt to this case)
These are the things we think about, generally, as technical debt, and try to address with things like design patterns, SOLID, and testing advice. If you'll notice, though, our definition of technical debt really doesn't have a whole lot to do with the actual code written.
Technical debt is what you get when you write code without taking the time to understand how it relates to other parts of the project.
Technical debt is a gap in your understanding of the system.
The problem here isn't actually in the code; it's in your head. Or, to put it another way, the currency of our technical debt is knowledge.
Debt is a great metaphor here, because in predatory lending arrangements, the interest you incur can quickly overwhelm the principal. The interest - or, in our case, the messy code - starts looking like the whole of the problem. But the original problem is still underneath, if less visible.
Design patterns and so forth mainly address the symptoms - the problems in your code, like the ones I outlined above. And certainly those are worth fixing, and sometimes, the process of fixing them will show you something that bridges that understanding gap.
But be aware that it is possible (and common) to refactor and not notice that the understanding gap is still there.
Have you ever tried a rewrite (of a system, or a subsystem, or a class) that had gotten out of control? You were sure you had figured it out this time. You knew how to break it up. You made a branch, told product that you wouldn't be working on any of their stories for a few days, and got to work.
Two weeks later, you `git reset --hard` and deleted the branch without merging. The bigger the refactoring you are attempting, the more likely that you'll miss the crucial details that would help you bridge the understanding gap.
A messy codebase direct to microservices is just one example. Other initiatives highly likely to fail:
* "Break up the User class"
* "Refactor the product data model"