Coding & story-writing is a good analogy. If you are inserting a new scene into a large anthology, you need to make sure the new scene fits with everything else. OTOH, writing a brand new story does not have this constraint / cost.
Which is all to say, software is an asset, code is a cost. The functionality of software is the valuable thing, all the code to do that is overhead. Thus, every line of code has a cost, and an ongoing cost that is forever then payed (which is exactly the interest; simply taking the time to scan/read over a line, to scan between 5 files vs 20, those are all ongoing costs)
Rushing out features with known bugs, poor architecture, no test and no documentation is akin to buying a house with a 20+% loan. You'll get your house sooner but you'll spend most of your resources servicing that debt until you can pay it down.
Another response gives the construction analogy (I try to avoid construction analogies for software building, because it's a bad analogy.. but I would digress - story writing & editing I think is a much stronger analogy for software development).
Let's say there exists two townhomes side-by-side (attached building). Let's say you need to replace one. The cost to replace one is a function of its attachments to adjacent buildings and overall build quality. If that other building were not there, all costs associated with compability would be zero. We can draw this analogy much further though, let's say to inspect the electrical lines - you need to remove half of the dry-wall of the existing building that you do not want to even touch. Had the electrical lines been put in correctly, that re-do and re-painting of the interior, the temporary eviction of tenants would all be unnecessary. This is the example of the overhead that comes with code. Anything that is 'attached' to something else is going to be more expensive to do anything with compared to it not being 'attached' to something else. The cost of considering and maintaining that attachment is not related to the value provided by that attachment, but instead to the quality of the attachment.
The 'debt financed' metaphor comes in when instead of doing something right, it's done badly which means anything that is attached to the 'badly done thing', needs to incur a high overhead cost to even just understand the badly done thing (every time, and whether we are even trying to modify it or not - this is high interest code). In comparison, if the system is decoupled and the APIs clean, that other thing might need only a moment of consideration instead (low interest code)
At the end of the day, it is to say - if you are replacing the 11th floor of a building, you have to consider every floor whether you want to or not. That is more expensive than if you did not have to consider those other floors. Considering the impact to 10 other floors is always more expensive than if those floors did not exist at all. The cost of maintaining those floors though is not a fixed cost, not all floors have the same maintenance cost. The maintenance cost of equivalent floors is not even the same, it's a function of the quality of those floors. Even high quality floors can be low-quality if we are trying to do something the designers did not expect. It's very context dependent, but all of which is to say that the value from features is significantly independent of the cost of the thing that delivers those features. The thing that delivers those feature might be very high cost, or it might be low - but it will have a non-zero maintenance cost. Simply reading a list of 10 items is more expensive than reading a list of 5 items. If anything, those extra "5" modules have at least that overhead cost, it's not zero. The debt financing analogy comes in to describe how high that interest rate is. Saying "code" is a cost is essentially just saying that all code has a non-zero maintenance cost (maintenance being the activity of reading, understanding, debugging, modifying, or deleting code).
The idea that "all code is technical debt" is saying that all code has a non-zero interest rate. In popular parlance though, we don't actually call code technical debt unless we think the interest rate is unnecessarily high.
To try and be more practical, logging is a good example. For some, cleaning up logs once every month or so is just a cost of doing business, it is not avoidable. Similarly, just adding a "grep -v DEBUG" is also just a cost of doing business and watching the logs. Someone else might look at this and call it technical debt, the lack of logrotate & overly verbose logging make all of the above unnecessary.
There are just lots of dimensions to it. To one person, having those DEBUG logs might have been the difference in finding a problem vs not, yet to another those DEBUG logs may never be useful and all they do is create noise. The interest rate we assign to code is incredibly subjective, depending on both task and person.
Sorry for being "that guy", but I think you meant "per se".
Technical Wealth pays dividends; Tech Debt charges interest on which the payments will eventually become unwieldy.
That doesn't mean you shouldn't take on Technical Debt, like many startups do with real money you can and should go into debt in order to get your business off the ground. But just like irresponsible financial management can kill your company, so can irresponsible technical management.
The idea that "all code is technical debt" because all requirements could hypothetically change someday, makes as much sense as saying "all currencies have no value" because 1,000 years from now we might not be using any of them.
Technical debt is not defined in terms of possible future requirements, it is defined in terms of current requirements. Code that elegantly and concisely expresses current requirements is debt-free. Code that is a mix of elegantly expressing old requirements, with new requirements expressed in a hacky fast way, has debt.
But trying to judge code in reference to all possible future requirements is utterly nonsensical. You can't measure that. It's literally meaningless.
So no, all code is not technical debt. Technical debt is technical debt, and clean code is clean code. Trying to redefine words to mean their opposite helps no one. That way lies madness.
Too often code is a proxy for complexity being added unnecessarily, consciously or unconsciously.
Premature optimization or over-engineering or burning your innovation points on shiny object syndrome can create technical debt in a different way than just taking shortcuts.
It isn't a profound thought to take a term like "technical debt" and just say well that's just everything.
Technical debt is absolutely that, adding something that incurs a level of interest, until you take the time to pay it off. In life, we often always have a level of interest to be paid.
It's a good analogy
Most of his code was actually technical debt. The code that wasn’t was external code he integrated. All of the code he wrote would eventually break down and until it did world force you to work around it, so it wouldn’t fail from interfacing with it.
In one of the standup’s i used the term technical debt to argue for fixing one of these issues that broke later but which he discouraged everyone to fix.
After that he integrated the term into his daily use to the point that it became meaningless. The people that believed this snake oil salesman thought that he had already carefully weighed his own decisions in advance.
Needless to say the issue was not fixed before he left the company and cost millions and plenty of customer headaches.
So yes, the core idea of technical debt resonates with developers, but not with business stakeholders. So if you really think the business is going to be okay with "we're gonna slow down our time-to-market and say no to all your requests for a while because we need to pay back our technical debt" (which is something I see happening over and over again), you're just keeping them out of the loop and taking over their responsibilities without actually having their accountability. Developers shouldn't decide whether time and money should be invested in time-to-market and quality or feature delivery, but that's effectively what's happening when business is not involved in the tech debt discussion. This only leads to friction and suboptimal results.
That's why I think we really need to be careful when using the words "technical debt" and not just label everything we don't really understand or think can be improved as "technical debt". Challenge yourself: what really is the problem with this piece of code, other than that we don't like it?
I think that if this is the case, you are presenting it wrong. Business types are very familiar with the tradeoffs of debt financing, you have present it as being paid in effort vs. $ (which after all, have a close relationship) and they will immediately understand that a large technical debt means you can get less desired stuff done for the same input of effort. And then you collectively decide.
Agree you have to be careful about what you present as technical debt. If you need buy in from the business side, you don't want them thinking "that's just what they say when they want more time, it doesn't mean anything". It has to be pretty concrete.
If that were literally true, then every new development would rationally necessitate a total rewrite of the codebase.
You might have meant that parts of the existing codebase would need to be changed to adapt to the new development, but as long as you've made a rational decision to keep using parts of the existing codebase, it must have been a net positive otherwise you'd just throw it all away.
Then the equation is simpler. More code = more to maintain = more liability. It says nothing about the quality of the code, just the quantity.
Tech debt is a trade-off between near term and long term ambitions - it is literally impossible to build a successful project without accruing this kind of debt, simply because you cannot perfectly anticipate the future. In that sense, it's not so much debt as it is a maintenance overhead.
-- Dijkstra in 1998 (https://www.cs.utexas.edu/users/EWD/transcriptions/EWD10xx/E...)
For similar sentiments consider "Science and the Compulsive Programmer" from 1976.
https://www.sac.edu/AcademicProgs/Business/ComputerScience/P...
"Programming systems can, of course, be built without plan and without knowledge, let alone understanding, of the deep structural issues involved, just as houses, cities, systems of dams, and national economic policies can be similarly hacked together. As a system so constructed begins to get large, however, it also becomes increasingly unstable. When one of its subsystems fails in an unanticipated way, it may be patched until the manifest trouble disappears. But since there is no general theory of the whole system, the system itself can be only a more or less chaotic aggregate of subsystems whose influence on one another’s behavior is discoverable only piecemeal and by experiment. The hacker spends part of his time at the console piling new subsystems onto the structure he has already built—he calls them “new features”—and the rest of his time in attempts to account for the way in which substructures already in place misbehave. That is what he and the computer converse about."
Even though I don't use any of his techniques, Dijkstra's "A Discipline of Programming" did affect the way I see things considerably.
The ideal software implementing a given functionality has as low of a carrying cost as possible. More code, in quantity and complexity, typically means more carrying costs.
One of the appeals of SAAS is that this carrying cost is easy to quantify and predict. Going further, for high-quality OSS projects like the Linux Kernel, the carrying cost may be 0 or close to 0 for most users. But lower quality OSS or buggy SAAS has a carrying cost in the form of bugs (possibly affecting downstream users), patches, need for support + back and forth.
In this analogy tech debt is a tradeoff for some short term benefit (like simplicity, time to market) at the expense of higher carrying costs down the line. But some carrying costs simply can’t be reduced - you can’t mooch off something like the Linux kernel, get your own carrying costs all the way to 0, or find a SAAS provider/vendor willing to give you the software for free. So in this model not all code is technical debt.
The under-appreciated corollary to that: tests are code, hence tests have non-zero carrying cost as well.
> The ideal software implementing a given functionality has as low of a carrying cost as possible. More code, in quantity and complexity, typically means more carrying costs.
You understress the complexity point. Higher quantity of code with high clarity and low complexity would have lower carrying cost. This is where DRY bites people: ill-suited or excessive abstractions can be worse than a little bit of copy-paste that are immediately visible to the eye.
> In this analogy tech debt is a tradeoff for some short term benefit (like simplicity, time to market) at the expense of higher carrying costs down the line.
In a general sense, doesn’t this hold for everything we do? If you really dig into it, is there an activity that is not a trade-off of short-term benefit for higher costs down the line (be it waste, pollution, something unknown or generally entropy)?
Also: nothing prevents us from deleting tests and do as if they never existed, except the nagging voice of sunken cost.
I've worked in so many places where a test suite suddenly takes a day to run with no parallelism, and requires 50 other machines to get it down to 20 minutes. And so many of the tests that waste so much time are down to them being pointless, or being stateful for no reason.
I constantly advise people to stop writing obvious bullshit tests (e.g. the kind of test in Rails where you just assert that you wrote `belongs_to :some_model` in your model), or ones that are too heavily mocked. Focus on your business logic and the inputs and intended outputs, not how you used a framework or dependency.
A car or a house also have ongoing maintenance costs, but they would normally be classified as assets (because they generally produce value in excess of the maintenance, or because you can sell them in a secondary market).
I think an accurate, useful, but not very catchy rephrasing is just 'working code requires ongoing maintenance'.
Different domains have different jargon. Code doesn’t map very well to finance because the loan is also the productive asset. It’s easy for a deprecated function to exist which literally nothing calls and therefore it provides you zero benefits, but such cruft is still an ongoing cost. Meanwhile that exact same function was critical last week.
I don't love this either because working code needs no maintenance whatsoever. After all it's already working. There's also no notion of "repairing" code because it isn't something that can just break suddenly.
So really what we're talking about is that necessary changes to the codebase can arise from not just the stakeholders as we typically define them but also because the environment around the code changes.
Someone who invested in shutters that can close instead of being purely decorative will be able to adapt more quickly and cheaply to a wind storm than someone who has to go out and buy wood to board them up.
And working code, no matter how well it works, imposes a cost to implementing new features. A new feature might be a cinch to implement in a new app. But it can easily take much longer to implement the same feature in an existing app.
Not all code. You have platform dependent code and then code that doesn't depend on anything outside of your own code. The code with no outside dependencies can easily stay around unchanged for decades and is not a liability.
"All dependencies are liabilities" I could agree with. Dependencies are technical debt that will continue to demand interest from you, they are often worth it but they are the main source of maintenance burden. Some are unavoidable if you need to interface with an API, but many are not.
That said, I agree that it's helpful to think of dependencies in terms of their potential liability as well as their benefit.
If you have a viable solution to a problem that doesn't require code, then you still get the value from that solution without the burden of maintenance.
Classic case in point: your in-house infra platform on K8S is an incredible liability no matter how much value it adds, because it's a means to an end for scaling and serving your product. Or a cost-centre, in other terms. You could just as well host on a collection of SaaS and offload a large portion of the maintenance cost onto the provider.
Rather than writing code that is easy to maintain (read: easy to keep), it's optimal to write code that is easy to discard. The latter is a lot more challenging than piling on architecture and in some sense it's akin to having illiquid code (stuff you can't get rid of very easily), and liquid code (stuff you can get rid of easily).
Here you're saying code is a liability because it has costs. But a car or a house have costs, yet they're almost always classified as assets, because the benefits outweigh the costs.
So you're left with a few choices:
1. Accept that code isn't always a liability, at least according to the dictionary definition of the word
2. Argue your way somehow to code nearly always being a net cost (seems unlikely, otherwise why write code?).
3. Redefine the meaning liability for this particular application
I think 1) is the most reasonable choice here.
An illustrative example where more code is a lesser liability: I work on a product with an activity feed feature built in 2018-19. There was a political battle on whether to build it internally or use a third party "feed as a service" product. The build it internally folks won. The "FaaS" under consideration went under during the pandemic, so we dodged that bullet. We also have customized it in ways not possible with an off the shelf product, with no big rewrite necessary.
On the flipside, we've never had time to implement things that are trivial with commercial FaaS like iOS and Android push notifications. Trade-offs are hard!
No - runnable programs are an asset.
The code to produce those runnable programs is a liability.
It's a necessary liability for a sustainable software business, as without the code fixing bugs is nigh-impossible (though not theoretically impossible - long-ago programmers wrote and read raw machine code).
But, the code itself is not what produces value. That's the code's output.
(Yes, you could be clever and say "but what about interpreted languages?"
I'd respond by pointing out that it's pretty rare to distribute a pile of executable code files to end users. Front-end JS only producing UX if delivered as part of a website, and similar principles hold for other interpreted languages - they're effectively object files once deployed, as in any deployment they're being run by the interpreter, not read by a human.)
> The code to produce those runnable programs is a liability.
That is total nonsense. So you throw away all your code after you built the program binary to avoid the liability? Don't think so, that code is a massive asset and throwing it away would make continuing the project unfeasible most of the time.
> It's a necessary liability for a sustainable software business, as without the code fixing bugs is nigh-impossible
Ok, so you agree with me, the code is a massive asset since it is the code that lets you change the program. Code is there to help programmers do their work, that is valuable as you say.
> But, the code itself is not what produces value. That's the code's output.
Both produces value. The code is valuable since it makes it easier to inspect and change the program. That is value. It isn't uncommon to sell source code access for products, why would anyone pay for that if seeing the source code is a liability? No, of course it is an asset, people arguing otherwise have just gotten too hooked on the "code is bad" mantra and try to argue all code is bad even though code obviously is very valuable.
Edit: And to be even more clear, throwing away the code and trying to work directly with a program binary creates technical debt, that is way worse debt than keeping the source code around. Code isn't inherently technical debt.
You're certainly right that when a business's customers include programmers who want to extend or modify the programs, the source code can be an asset. I was mistaken to say categorically that code's never an asset.
On average, though, I think it's still more often a liability on the balance sheet.
It's why we lionize programmers who cut out thousands of lines without removing any features - they reduced the liabilities while retaining all the assets.
Only a couple days ago I decided to change some data structure. It was needlessly hard because I had neglected to create an accessor function and was instead accessing it directly everywhere. Had to change every bit of code that accessed the structure's fields.
That's technical debt: something the programmer should have done but for whatever reason didn't. I paid back the debt by doing it: I created the function. Now if I want to change the data structure I only have to update the function. If I hadn't paid it back, it would start accruing interest: it would be even harder in the future as more and more stuff accessed the fields.
Here, heed Ward words directly, poor programming is not debt, and debt doesn't cover "whatever reason programmer may have" but has a specific well defined scope: https://m.youtube.com/watch?v=pqeJFYwnkjE&t=198
This is the same for bog standard development though. Any code you add, regardless of whether it is termed technical debt or not, will make your future development slower.
Like you say, that doesn’t mean it’s the wrong move, you want to build a product after all, but I think I can see where the author comes from.
Imagine I'm trying to do a bunch of graphics stuff. The code I write to integrate a 3rd party graphics library will make my future development faster. I am effectively creating a lever I will later use to increase my productivity.
Say I need to do a bunch of linear algebra. Code I write to calculate dot products, determinants etc (or better yet to use a library to do those things) will greatly accellerate progress later and enable me to think in terms of the linear transformations I'm trying to accomplish rather than the underlying primitive operations.
I think "technical debt" as a term is overused and sometimes misapplied, but the core idea resonates with many developers, at least the way it is most often used - which isn't exactly the original proposal as another commentpoints out. The key part of it is the analogy with an interest rate. I can decide do put 5k on the credit card for a last minute holiday, but for as long as I don't pay that off, I'm paying 20-something percent, every month, forever. Similarly for example a rush to add features to hit a demo deadline can introduce brittleness and abstraction failures into your codebase that you pay for every time you make changes, add features, do nearly anything. The analogy is that some of your development is going to be wasted "servicing the debt" going forward until you fix that (pay off the debt). That doesn't mean its the wrong move, for the same reasons that taking on debt can be the right move for a person or business.