I compare technical debt to debt.
You may go in debt because you looked at the housing market and decided that now is likely a good time to buy a house with low interest rates. You take on a large load of debt, but it is done wisely based on currently information.
You may go in debt as a business operating on credit to fund an expansion that should pay off quite nicely in a few years time. Maybe it will fail, but it isn't a bad risk based on current knowledge.
You may go in debt for an education. Depending upon the degree and extent of debt this could be good or bad.
You may go in debt by spending beyond your means on a credit card. This is generally unwise.
You may go in debt because of a medical emergency that is entirely beyond your control (especially in some countries, but even some countries with universal healthcare it is possible to need treatment that isn't covered).
All of these have something similar when dealing with technical debt. Sometimes it is a wise investment. Sometimes it is a risk that may or may not pay off. Sometimes it is beyond one's control. Sometimes it is poor planning that will be regretted later.
This assumes all things can be usefully viewed through the prism and language of economics (though my gut feel is few things can or should be).
Making progress in the world is fighting against entropy. Just because it is hard to fight the disorganizing efforts of the universe doesn’t mean that your output is a liability.
> liability: a thing for which someone is responsible, especially a debt or financial obligation.
According to the balance sheet equation,
> Assets = Liabilities + Owners Equity
Certainly all code is a liability - it must be at least understood and maintained from time to time (80% of lifetime cost is in the maintenance).
And while an accountant might also mark it an asset - not all code is an asset - and we recognize that fact when we delete for badness or disuse - or rewrite it to make extending and maintaining it easier.
I'd accept that debts are a subset of liabilities, but not that 'liabilities are debts'.
A debt implies an externality, while a liability implies some generic exposure -- it's that which I focus on when considering (all) code as a risk to an organisation.
I suspect, however, that we agree on the fundamentals. There's some cost and risk to developing and trying to maintain any code base, and accepting that can usefully inform development and operations work better than thinking of code as a (near-)risk-free asset.
I used to program COBOL on IBM 370 DOS/VSE systems they later went to Visual BASIC on Window 3.1 PCs. The conversion took a long time and most code had to be rewritten because COBOL is not BASIC. Management didn't care about that, they just wanted a working project. We all did our best going overtime without pay and working weekends to meet deadlines.
COBOL was a liability when they wanted to convert to Visual BASIC. There was liability to convert the code, convert from DB2 to SQL Server on the database end.
Technical debt is configuration and plugins. You are able to deliver something now with the benefit of changing behaviour later but at the cost of having an enormously increased test surface and invasion into your type system with possibly hostile parameters.
You're betting that the maintenance foregone in the present is paid back with interest in the future.
This "pay back" could be the success of the company which allows you to pay for more developers and tools.
Is it worth it? Time will tell.
If that were the case, the company would have a negative value.
Code is asset that requires maintenance.
But yes, it's definitely worth reminding ourselves of the ongoing cost of maintaining code.
I reconcile the concept that code is a debt with your statement in the following way:
The feature produced by a piece of code can be an asset, but the code itself is a debt.
Thus, for a new feature to be valuable, you want the value added of the feature (or asset) to out-weight the cost of the code (or debt).
As a general rule, the complexity that comes along with a piece of code grows as the code-base and feature set grows, thus, for a young codebase, the value of a feature more easily out-weights the cost of the code.
'Technical debt' is just not debt, it's just an interesting way to think about it, and a neat use of lingo.
There are any number of other business practices for which one could try to apply the same analogy, but it wouldn't work because very quickly the term wouldn't seem right.
For example 'training employees' for a situation, knowing they might need to be trained differently in the future, is not going to be regarded as debt.
Same thing for processes, investment in equipment, parts, materials etc..
Although, you could say that a lot of profitable companies have negative value codebases where the cost to rebuild them is less than the cost to maintain.
This probably explains why some companies get replaced by small nimble startups.
I think it is. Technical debt is all about trading current productivity for future productivity. Poor architectural decisions can be caused by several factors. Inexperienced devs, not taking enough time to understand problem, not spending enough time brainstorming the problem. These are all decisions that trade current productivity for future productivity, which is technical debt.
Also most technical debt is not accrued consciously. Very few people are doing cost benefit analysis on technical debt. It's usually made intuitively by project and product managers who don't directly interact with the costs of technical debt but directly experience the benefits.
This is a very succinct and accurate description, and one I haven't come across. Thank you for this phrasing :)
Requirements changing is also a big one. 4 years into one project that I had created and maintained a "one to many" relationship changed to a "many to many" in some of the tables central to the system. Something that I had clarified wouldn't when originally designing the system.
sure it is, in the same way that taking one of those payday loans to cover a coke habit is still real debt, it's just real bad debt.
Besides which, it trivialises the seriousness and the tragedy of addiction. If you think your tech debt is similar to that you are well equipped to quit your job and work elsewhere. An addict does not have such an easy choice, they can’t leave their addiction to someone else and move on.
You choose to take the payday loan or hit up a shark, addiction or no.
The point where I concur is when you gamble the short term against the future despite all evidence that you will benefit from spending time to save time.
Agreed. The addict might recover. Organizations with poor culture are doomed.
That's not what "technical dept" is. Choosing a quick solution instead of a proper one that would take more effort is. I've made that conscious more than once myself: to hit a deadline, or because I didn't know if the MVP would even survive long enough to worry about code maintenance.
After the MVP, you may wish to add more features or support more users, it is at that point that you can assess what is debt and what is not. It's not a matter of "cruftiness" but a matter of what's holding you back from adding additional features or supporting a larger number of users.
If we frame all work as doing something that will deliver some sort of value (be it customer value or unlock the potential to add more features to add customer value), then it becomes a more constructive conversation about work that needs to be done, debt or otherwise.
I'm not a fan of just looking at whether code is crufty or not to determine if it's "technical debt". If you look at code long enough, you'll find something you don't like and want to rewrite, but it has to be in the service of some other goal or else you'll just become an architecture astronaut building wonderful things that don't provide value.
Edit: as an example that comes to mind, a bank running on COBOL doesn’t have tech debt unless their COBOL engineers are aware of their missed opportunities. If they think COBOL is no longer fit for purpose, it’s not debt, it’s time to reinvest.
Should there be an analogy for depreciation? Some of the "best practices" in my current C++ shop are in direct contradiction to the Law of Demeter and related philosophical stances in Object Oriented programming (particularly ones espoused by Martin Fowler) despite the fact that my current shop advocates for OO. Neither the old position or the my current shop's position is so bad that it's not viable, though some hotheads in the field act as though this could be the case.
A lot of the churn seems to be arbitrary and driven more by fashion than solid first principles. (Which would include cost-benefit analysis, for which there is often insufficient data, and for which no one wants to pay the cost to gather the data.)
For the cases like legacy code and turnover, perhaps "tech depreciation" is the more appropriate economic term. You didn't really borrow against the future to have more today, rather something naturally decreases in value over time.
I think there's a lot of value to be found in separating the known debt from the unknown debt though, and giving it a name. I might have to start doing it.
So thanks for making me think about it.
The cobol example is great - it is code still solving the problem but is no longer efficient or productive to find and pay cobol programmers.
What you have instead is knowledge debt as engineers turn over, and the once intuitive and elegant parts of the codebase are utterly alien to the new hires who learned their craft in a totally different way. This isn’t tech debt because engineers can’t intuitively anticipate the business’s future.
That is it! Thank you for sharing your thoughts.
You make a choice to accumulate knowledge debt by not writing docs, not cross training team mates, etc.
This, and I will accept being wrong, is why I think we see code we didn’t write and immediately see how we would do it better. What we’re doing is sending ourselves back in time and replacing their shoes with our own. They didn’t know what you know and you have no idea what they knew.
A potentially effective approach is to introduce an immutable decision log. We have these people in many other fields - they record the decisions made and they become part of the overall reasoning.
I reckon anyone in a position of power should have an independent decision log. Tech architects, CTOs...
Doesn’t this assume that code as written was of the highest quality? Under tight deadlines or misaligned priorities code quality suffers. Even if it works or “is right” doesn’t mean it wasn’t done to a standard that maximizes productivity.
If they consciously chose to sacrifice on it, that’s the debt.
The time it was written often isn't when the technical debt was incurred, when maintenance to keep it right, technologically, for the changing time was deferred, that's when a decision was made to incur a higher price later to save now: that's when the (technical) debt was incurred.
> as an example that comes to mind, a bank running on COBOL doesn’t have tech debt unless their COBOL engineers are aware of their missed opportunities
The existence of technical debt is unrelated to awareness of the deferred costs. “technical debt” isn't a statement about the existing technical staff’s perception of the system.
You know, if there only were a way to pass knowledge from one person to another aside from directly talking to that person. Perhaps if we had means of preserving knowledge, and even making it a part of the job description... But alas.
Joking aside, not writing technical documentation and product design docs is very much technical debt.
A lot of information that is lost with turnover is information that could have been written down and passed on.
Documentation is not jut code comments. It is:
-A solid bug tracking system, with clean and reproducible bug descriptions
-A version control history, where every change has a why in addition to a brief how, and which links with the bug tracker
-Well-written tests, which not only verify how a system works, but showcase how to use the APIs.
-Documentation for tests explaining why this input was chosen, what kind of output is expected, and why it was written that way
-High level comments in code (especially libraries) explaining how the API should be used
-High level documentation explaining the structure of the code that is checked in, and lives with the code. The documentation can be in Markdown or a plaintext README, but should be there
-A "code map" telling you what the code in various directories does, and where to look for things.
-And, most importantly, a lot of design docs - for every feature and algorithm. "We are solving this problem. Here are the alternatives considered. Here is what we are going to do and why. Here is how we'll implement this. We used ideas from this, this, and this paper".
Yes, all of that is needed. The price you pay is engineering time. But if you take on the debt, your new engineers will have Hours Of Fun[1] trying to figure out, say, whether the imaginary nanometers[2] model parameter increasing as a result of their change is a good or a bad thing.
What I am saying is documentation debt is technical debt. Documentation is just as important as the code. And having it does help[3].
You might think that your code doesn't need all of that. And that's fine; but that means you plan on never paying that debt. Maybe you plan for your startup to fail/sell out in a few years. Maybe you plan to leave that job. But anything long term means you have to write things down.
And if you haven't been doing so - now is as good time as ever to start.
[1]http://wiki.c2.com/?HoursOfFun
[2]True story. There was a good reason for why the distance parameter had complex-valued nanometers as units; it was an elegant way to do improve a model while staying backwards-compatible. It was written down nowhere, and have fun googling "complex nanometers". They only exist in that code, AFAIK.
There is information that must be experienced, drilled, and committed to habit and muscle memory: tacit knowledge. Drill, practice, experiment, coaching. Often 1:1 or at least 1:few.
It's highly arguable that individual knowledge does not become cultural knowledge until a means of transmitting it from one generation to the next becomes both established on institutionalised. It need not be a large institution, but it must have persistence.
Within any knowledge domain, the explicit knowledge transmits more readily than the tacit. With time and complexity, I suspect a situation much analogous to Amdahl's law is reached, in which generational knowledge transfer comes to be dominated by the tacit component -- it is harder, slower, and more expensive to transmit, and tends to irreducibility.
Technical cultures which become or incorporate a tradition of shared stories and lore, as Unix and Linux have done, seem to achieve this better than others.
And that neglects issues of semantic and cultural drift which affect written texts.
Though I'd come to this conclusion some years ago, I'm finding reading Mortimer J. Adler's How to Read a Book that this is a significant theme of that work.
“Legacy code” is usually a label applied exactly to identify that code which has been determined to represent technical debt.
I found it be a pretty accurate summation that gives some perspective from the other side.
The two categories overlap, but not all code generating revenue fits in the traditional scope of “Legacy Code” and not all “Legacy Code” is generating revenue.
> I found it be a pretty accurate summation that gives some perspective from the other side.
What “other side” are you referring to?
Technical debt that does not generate revenue in any way is often far easier to replace/refactor.
I look at legacy code like this:
- Could we remove this from the product? - Could we replace this part of the product with a totally new system? - Are we OK with potentially introducing subtle bugs in this code as we work through refactoring it.
If the answer to the above questions are “No”, it’s likely revenue generating code (or part of some compliance process).
In this case the “other side” is non-technical stakeholders who don’t inherently understand the importance of resolving technical debt.
If you just say that some code is bad you will get unproductive reactions like people getting defensive, trying to assign blame etc. Calling it technical debt allow you to call attention to an issue while avoiding blaming anybody.
Making poor architectural decisions or writing poor quality code isn’t tech debt. They are just institutionalised consequences. The way the world now works.
Legacy code isn’t tech debt either: it was right for the time.
What you have instead is knowledge debt as engineers turn over, and the once intuitive and elegant parts of the codebase are utterly alien to the new hires who learned their craft in a totally different way. This isn’t tech debt because engineers can’t intuitively anticipate the business’s future hiring decisions, particularly when those are made independently of the team.
If you want to establish tech debt you have to define the schedule to pay it off and there should be some record of that work somewhere, with accountability attached. It is not merely the accumulation of things you don’t like the look of or find hard to work with.
Similarly, I think MVP is a conscious choice to incur product debt because it is far too risky and irresponsible to go all-in with your perfect vision right off the bat. You want your initial buy in and a chance to bail out or pivot before all is lost.
Edit: as an example that comes to mind, a bank running on COBOL doesn’t have tech debt unless their COBOL engineers are aware of their missed opportunities. If they think COBOL is no longer fit for purpose, it’s not debt, it’s time to reinvest.