Technical Debt Is Like a Tetris Game
fluentcpp.com
fluentcpp.com
There is nothing wrong with taking out a mortgage (and thus getting into debt) to buy a house - you just need to understand how to pay it off. Similarly, if you start a startup trying to find product-market fit before running out of money, there is nothing wrong taking on technical debt to do so, as long as you understand what you are doing.
I like the Tetris analogy because that game has a type of debt that anyone who has played it can understand, yet is not as easily quantifiable as financial debt.
So, you mean today? The number of students that take on crippling amounts of student loans without truly understanding how it will affect them is staggering to me. The ease of obtaining credit before a person truly understands how devastating 20%+ interest rates are is also pretty telling. Financial debt is easy to get wrong, and is only compounded by the fact we don't teach this as a basic course in primary schools.
Or just large numbers. A valid strategy to invest a million will be worthless with just a dime. No matter what the silly myths of success say.
If the debts were thoughtful and deliberate with understood risks, they might make perfect sense.
If the debts were taken by people who couldn't evaluate them and had no business making that decision, it's going to be bad.
Odds are the first are well-understood and maybe even documented. The second may explode when the underlying assumptions or risk changes.
You might owe a debt to the mob. Perhaps they help you out of a sticky situation but let you know that some unspecified favor will be required in the future. So you have a debt, but what exactly you owe is loosely-defined and might cause great chaos in your life at some inconvenient point when it's time to repay.
Engineers sometimes want to overengineer things that would work just fine in that situation. I remember an old link about someone studying the source code of a game (Doom maybe) expecting to find beautiful solutions for every minor problem when in fact he was surprised by how most of it was straightforward.
Check what needs to scale/be resilient before reinforcing the code there (and of course, if the solution is already frail at the moment it probably needs to be fixed sooner rather than later)
(bonus: write your code like the next person to work on it is a psychopath and they know where you live)
For business school grad PMs I like to see if they can run with an analogy to selling unhedged call options rather than thinking of it in terms of debt with a known payoff schedule.
Even the bankers got derivatives wrong at an epic scale.
Technical debt is not well understood, and its impact is not easy to measure.
This analogy is probably why so many companies make the (usually wrong) decision regarding technical debt.
I think this metaphor does a good job of capturing the path-dependant nature of debt. How the size of each individual debt sits in the shadow of the code that relies on the flawed code. Understanding that there's the work of fixing technical debt and the work of getting to the place where you can fix technical debt feels more clearly communicated by tetris than by financial debt.
That kind of debt, right?
But wait there's more! I forgot to mention that even if you walk away you get to keep what you bought on credit, without any further payment of any kind and it continues working in its current state, forever - you just can't improve or renovate it if you walk away from the debt.¹
That kind of debt, right? If so then perfect analogy.
-
¹ literal lie though, yes you can. you can totally still improve it real quick. I'm afraid I'll have to add $150,000 to your technical debt though, which you can walk away from any time. still want this real quick $50 change though? It might be $50 but it's $150,000 in debt though.
So, excluding the actual strawman numbers, analogy still holds very well.
I "defaulted" on the loan of replacing a script I wrote in 2 minutes in autohotkey (a windows scripting language). I'm never going to write it properly.
Now explain to me how defaulting on this technical debt has hurt my credit rating.
Is the next time I open a new autohotkey script is it going to say to me "Now wait a minute. What about all the debt you defaulted on last time? I'm not going to let you write a quick and dirty script! You'll default on ever rewriting it! Go do it properly as a source controlled project in Microsoft Visual Studio. I am not letting you write another script."
Is that what happens? No?
That autohotkey script is running now. Where's the debt? How is my credit rating worsened by defaulting on it?
It's debt you can default on at any time without any consequence, while being able to continue to use what you bought on credit.
Your version is like saying that the minute a squatter takes up residence in an abandoned building, he's just gotten $140,000 in debt, the cost of buying some land nearby and building a proper, albeit unfinished building with no electricity or heating.
And if, still in his abandoned building squat, he finds and burns some wood in the winter, watch out! He's just added $25,000 in debt to the $140,000 he already had, since that's what a proper HVAC system will cost in his properly built building. He went from $140,000 in debt to $165,000 in debt the minute he set fire to some wood.
But that's clearly very wrong. Maybe he'll live in that abandoned building for 4 years and then just walk away. Poof. The $165,000 in lousy-analogy debt disappears the moment he walks away.
It's not debt. It's a lousy, bad analogy.
Most of what people call debt, though, is really just comprises. Which works, to me, as you can just say the code is too compromised. :)
You can write the shittiest spaghetti code in the world, and if you never have to build upon - or touch it again - you took on zero technical debt.
In comparison, if you write just slightly flawed design, but build out your complete featureset based upon that flawed design, you could have mountains of technical debt.
I think a better analogy is building a structure. If your code is foundational, you better get that shit right. If you cheap out on a $20 part and have to replace it, but it's just a door knob, that's not too bad. But if it's under your slab on grade foundation you're going to pay thousands to dig it up and fix a $20 part.
> Technical debt consists in performing a fix or development in a quick and dirty way because it takes less time, even though it will make the code harder to work with in the long term.
If you aren't going to be changing the code later, then writing a bunch of spaghetti code won't make it harder to work with in the long term.
Otherwise, you always have to deal with it eventually. Look at the Y2K problem. or how the 2038 problem is starting to crop up already, exposing significant levels of technical debt. https://twitter.com/jxxf/status/1219009308438024200
I also think technical debt does accrue interest over time, albeit not as linearly as a modern installment-plan debt does. That's because it slowly falls out of sync with people's memories. With libraries, with operating systems, with techniques. Bad code I wrote 10 minutes ago is pretty easy to clean up. But the next day, the next month, the next year? The cleanup gets more expensive. At some point the work becomes more archaeology than normal programming.
It's also true that actual foundations have something in common with foundational code. But that's a different aspect of programming than what tech debt is pointing at.
> Most operating systems designed to run on 64-bit hardware already use signed 64-bit time_t integers. Using a signed 64-bit value introduces a new wraparound date that is over twenty times greater than the estimated age of the universe: approximately 292 billion years from now, at 15:30:08 UTC on Sunday, 4 December 292,277,026,596.
There are plenty of counter-examples, but I like the example of video games.
https://www.polygon.com/2020/1/13/21064100/vvvvvv-source-cod...
There's a pretty clear ship date, after which you never touch it again.
Even in the world of SAAS, there are plenty of services or libraries that you write that serve a single purpose, are good enough, and never get touched again.
Even the code you mention isn't a great example for your case. In theory it was throwaway, but now that it's out there, you can bet that plenty of people will be touching it. And you're also ignoring the path not taken. This beloved game didn't get a sequel, despite plenty of demand. If the code were in better shape, might it have had one?
https://technology.riotgames.com/news/taxonomy-tech-debt
It breaks it down along three axis:
* Impact. How much it affects things.
* Fix cost. How hard is it to fix.
* Contagion. How much it spreads over time. If a piece of tech debt is well-contained, the cost to fix it later compared to now is basically identical. On the other hand, some forms of tech debt keep giving. Image if your string type has some issues.
Linking that back to the debt analogy, there are different interest rates for different kinds of debt, depending on how contagious that part of the code is.
I don't really agree with this view; it may seem pedantic, but I think it's better to consider that case as tech debt.
In my experience, it's incredibly uncommon to write code and never touch it again -- particularly if you elect to cut corners in the interest of getting something out the door quicker. That code inevitably either gets retired because it's not being used (your debt was forgiven), or gets built on top of (your debt starts to compound).
If you make a decision to lower the quality bar because "we are not going to touch this again, so it's not tech debt, so it doesn't matter", then I think you're fooling yourself. Instead, I think it's better to assume that you're taking on tech debt (you almost certainly are), apply all the best practices around managing that debt (i.e. catalog it, be aware of your overall debt level, and be aware of tech debt in critical systems), and be happy if your debt is later forgiven if you miraculously never need to touch that code again.
Maybe there's a narrow sense in which this position is technically/semantically correct, but I think more often than not, this position is going to hurt you more than it helps.
Cleaning up one kind of issue in your code doesn't affect other kinds of issues, and you can end up with fundamental architectural problems that no amount of testing or clean up can compensate for. You can handle some issues incrementally (eg adding tests), but some issues are going to be so fundamental to your system that you can only handle them by making a lot of changes all at once.
You can't refinance technical debt!
One of which is that it is often really hard to quantify, but that does not seem to stop many non-technical manager types from trying to treat it like financial debt nontheless.
Another one is that unlike financial debt, which should be controlled by various organization-internal mechanisms, tech debt is often not something management is conscious about (at least when it is "created"). I've seen far too many examples of horrible code that can simply be explained with lazyness or incompetence, but I'm pretty sure management never called for a hard to use, unnecessarily complicated API.
Also, if the tech debt is caused by management, it is usually an implicit byproduct of unrealistic time constraints, and I'm pretty sure in those cases they would often choose find another solution too if they were conscious about how bad the problem they're creating really is.
Edit: Either that, or their incentives are way off because they don't have to deal with the consequences. Which is also the real cause why financial debt is usually strictly controlled.
1. You wouldn't pay good money for it if you knew what was under the hood.
2. Most mechanics are reluctant and embarrassed to work on it.
3. Maintenance is going to cost more than you think, probably by a wide margin.
4. You wouldn't expect a major manufacturer to ship a brand new vehicle this way.
5. Nobody who knows what they're doing will ever brag about having a hand in creating it.
If the individual(s) with power over ones work have spent time writing code then they will most likely have an understanding. If they haven't then it often takes analogies of this nature to help them begin to understand what technical debt is and how it has an impact on the future of the product.
One thing I've never seen properly stated is the difference between technical debt as usually described (building code that is doing the job but might not be as flexible, easy to change or fix or might not account for all corner cases appropriately) and then the kind of debt I've experience more often which is "model debt". In the field of business applications, modeling a problem correctly is 80% of the solution, and new companies usually do that job very poorly. Over time as business experience accumulates, some of those mistakes become evident. For example, how to model the identity of the users of an application, that's where the most mindless decisions I've experienced do the most damage. Let's say you assume the only type of user you're gonna have belongs to "companies" and your customers are companies. Then you discover those companies make heavy use of consultants or contractors, and all of a sudden most of your management tools and UI doesn't quite fit. You can hack it, but you end up with a large slice of your users having to manage 20 different emails to interact with your system. Just an example.
"model debt" is the most expensive and potentially fatal type of debt a new piece of software can incur, and my suggestion is for every new project, focus on building a real good model of what reality looks like before too many other technical calls are made, or you're in for a world of hurt.
The art is to find the sweet spot.
This metaphor cannot be stretched too far, though, since the actual tower has the leaning as one of its main attractions, whereas in your project, the technical debt is best hidden from tourists (customers). :)
It amazes me the number of people who think metaphors and analogies are literal copies of the concept they're approximating and thus proceed to nitpick those comparisons.
I dont know about that... Eiffel tower is vertically straight and sells a lot of tickets.
Yeah, but in Italy those are a dime a dozen.
> The analogy with technical debt is that each new fix or development is like a new block coming in, which you need to integrate with the existing code. If you hack it in a quick and dirty way, it’s like leaving holes in the Tetris structure: you’re making life more difficult down the line.
Until that player departs, and the team is buried.
An unskilled one might see a lot of it that doesn't even exist.
Is Duff's Device technical debt?
In reality there is a lot of tech debt you can get away for free and never have to pay back. The code can be a mess but the functionality works and doesn't need updating.
Other tech debt can infect and cripple a system if it isn't contained early.
If you demonstrate that you know how to prioritize the most important tech debt then it is easier to convince management to work on it.
The concept of doing more work now to make future easier is universal. Delivering on it is the hard part.
Not an original idea but I can't remember where I heard the idea. Technical debt is the best kind of debt, because it's the only debt that you might not have to pay back.
Think about this famous analogy: "Life is like a box of chocolates. You never know what you're gonna get."
I clearly know about how life and a box of chocolates can be random at times. This is dead obvious information to the point of pointlessness. Yet in church or in meetings you see people nod their heads in agreement as if something insightful was said. Here's reality: Nothing insightful was said, the person who was comparing life to a box of chocolates is literally mentally retarded.
Analogies are used to manipulate, not to inform.
That's a very low-tier understanding of Tetris. At an intermediate level of play, you'll want to deliberately leave gaps that you could clear at any time, but you choose to wait until it is strategically advantageous to do so.
At an advanced level of play, you'll want to leave oddly-shaped gaps that can only be cleared with a Clever Hack which not everyone is able to understand, let alone perform. (T-Spin)
Similar concepts do exist in programming, they are cynically referred to as "Job Security" but I doubt that the author intended to encourage that sort of behavior.
I believe that learning how to identify a team that is drowning in tech debt is a valuable interview skill.
Reading the table of contents of the book, it seems to assume everything is done by hand. There are some power tools for dealing with legacy code in C/C++, but I don't know which ones are any good and for what. A book which covered most of them and wasn't a fan piece for one tool would be helpful.
It's a lot harder for someone to say "I didn't know how to solve this" and much easier to say "It would take longer to solve it the right way." Because of this you should take claims fo "Technical Debt" with serious suspicion -- and given how fuzzy the general concept is and how enabling it is, I think the proper response is to find it useful far more for personal ego management than as a respectable tool for thinking about execution of software projects.
There is no innate conflict between solid decision-making in a software system and execution time. Generalizations take longer but generalizations or lack thereof do not imply sound architecture; this is orthogonal concerns. But solid execution (decoupled components) is a skillset question. In fact if you have the skillset it often takes longer to make a bad decision.
Technical Debt doesn't respect this (i.e. skills/mastery) and is only good for deflecting from our incompetence and thus keeping us from acquiring the necessary skillset.
Implementation technical debt (the code) makes it harder for devs to work in a given service, slows down velocity and increases the risk of bugs, but is at least contained to a given service and can be rewritten as a known quantity.
Architectural technical debt will crush your product or your stack, often quickly.
There will almost always be technical debt to some degree when it comes to software. Nothing is perfect and we always dont have the time to create the perfect fix..and instead need to just create something that works.
Sure, that balloon is just sitting there, but every single day you don’t pay it down, you are still paying interest. It slows you down when it causes confusion, when it makes integration more difficult, and when everyone is afraid to make improvements that might require changes to That Godforsaken Class.
And if instead of paying it down, you decide to add more debt, then the interest payments go up / the drag just gets worse.
And let’s be honest, arguing that tech debt is not bad comes up way more often over adding more than deciding not to start paying down.
https://medium.com/s/story/technical-debt-is-like-tetris-168...
Although he references a 2017 article that says the same thing so I guess it is a meme floating about.
Can anyone explain to me what reflexion means in this context?
But to reflect on something exists in English as well, just spelled differently.
And then everybody needs a better metaphor to explain the bad metaphor to non-technical people.
(One of the better games on there. Not a large selection.)
Known and unknown.
If you are not knowingly accumulating technical debt, then 90% of the time you are accumulating it unknowingly. This is the most insidious form of debt.
I have searched my entire career in software engineering for the solution to unknown technical debt. The problem isn't just solving it, it's also clearly defining what is happening to your software over the years even when you spend the utmost care in not allowing technical debt to accumulate.
I think I sort of nailed down what's going on in a fuzzy way. I also sort of have a fuzzy solution to the problem. Allow me to elucidate:
Eventually most projects will hit a point where you realize that your initial design was poor. You are given a new feature and your old design is simply not flexible enough to accommodate the change. In order to put the feature in, you either do a massive rewrite or you hack it in.
In this way the technical debt that was accumulated unknowingly may force the programmer to begin accumulating debt knowingly. In fact they often forget about the unknown technical debt. They think that the only proper solution is to contemplate between the cost of a hacky shortcut or a massive rewrite without thinking about what caused the need for the massive change in design. What exactly is the thing that is causing the "need" for the massive rewrite?
What's happening is, whenever you design something you are predicting the future. You are assuming your design can handle certain current requirements and future ones. However, the future is actually unknown, there is very little chance you can ever predict the future in an accurate way. Thus by your lack of ability to predict the future, your design will usually in the majority of cases fail to predict the future and this problem will always occur.
That is all that is happening with unknown technical debt.
The best solution (not a full solution) to this is to design all your projects with the maximum flexibility possible. That means every corner of your code that can be factored into multiple modules or be easily factorable in the future. By coding this way, you maximize the reusability and reconfigurability of your code giving the best possible insurance for an unknown future.
If you heard of ravioli code, this is it. It has bad connotations but ravioli code is my best known solution to unknown technical debt. Most people feel ravioli code is hard to read or maintain... In a way it is, but the key here is deal with the program in layers of abstraction. Your complicated ravioli code of 100 primitives at the lowest layer must be able to "compose" into higher level layers where you only have to deal with a manageable 20 primitives.
So the key isn't just ravioli code, but ravioli code with highly compose-able modules you can use to simplify your code into layers. Think of it like a slider between flexibility and simplicity. The higher the flexibility (more primitives) the more complex your program is; the lower the flexibility (less primitives) the more simple your program is... A good design is one where at any moment in the projects life time you can move from layer to layer and readjust the flexibility and simplicity of the program at will.
Unknown to most people there is a programming style that does the above almost mechanically:
Typed Functional programming.
You may have done some FP and seen it get messy (especially with JS), but to really see why this is the solution you should take a look at Typed FP using the point free style. The point free style will help you understand the true nature of a compose-able primitive.
In my experience the people making the debt don't use the term as much as the people pointing out the pile of ignored work.
... or wants to get away with shitty work because they don't want to admit they don't know how to do it right and be vulnerable ...
You do everything in your power to explain the compromises that were made to the CTO and the CEO, and they weigh their options and determine that they want to keep duct taping things together as fast as possible to keep making more of these quick sales.
Building things "right" the first time is a luxury that is rarely possible in the real world, I've found. Maybe if you are working at a big company like Google or Amazon? But from what I've seen, even the projects coming out of those companies are often deeply flawed and have have a plethora of issues when they are initially released.
The only thing that works with technical debt is to aggressively uncover and strike down any short term incentivisation from an organization perspective, and to set in absolute concrete terms what the irrevocable code maintenance policy is, and punish those who don't follow it. Everything beyond that, TD measurement tools and TD backlogs (which you interlace at a minimum ratio) are just details.
But the last thing that'll help you is talking about Tetris in front of your client / PO / boss / whatever. Just call it "maintenance work". And if they're hostile to even that, it means they're stupid enough that you can just do it anyway and call it something else and they'll never know.
That is quite true. Unless you are working in a jira ticket sweatshop just do it.
They can be highly successful, but from your comment it sounds like you've dealt with managers or clients who simply want to "win the conversation". In that case I can understand why you'd prefer to take care of the necessary remediation more covertly.
Doesn't mean others won't benefit from apt metaphors though - bad collaboration partners sounds the issue here, not figurative techniques per se.