Hacker News new | ask | show | jobs
by crdrost 1605 days ago
Code doesn't deteriorate... Like we're talking about a banana growing spots or whatever...

Code gets plastered over with features and abstraction layers, but that's an active process that we are complicit in doing.

More germane for this discussion, technical debt was originally defined as a positive thing that you want to go get... It is the mismatch between our domain model and how our users think about the domain. It is positive because “enough with all of the planning and interviewing and requirements gathering and careful architecting, can I just build something and have my users criticize it and do four or five drafts until I get it right?” ... The debt is the drafts before you get it right, the interest is the constant translation between the language of your users and the language that the system is expressed in. You have a “contracts” table that contains things that are not contracts because every purchase foreign keys to a contract, but your users have since wanted to know what they do with the purchases that are not associated with any contract, where do they go. And now every query that aggregates over contracts needs to exclude the non-contract contracts, this is part of the interest you are paying.

But at some point it started to mean that we had to upgrade our dependencies, that's tech debt, or this kludge that I threw in, that's tech debt, or the fact that we never worked out a shared library between the front end and the back end and so any Python code in utils.py needs to be manually kept up to date with a file utils.js in a separate Git repo so that we are sure that we can do these things both on the front end and the back end. More broadly, anything that we no longer care for is tech debt. And that's where you really get this idea that it is deteriorating, that's more a measure of our own patience deteriorating, especially as we never seem to have time for the refactors we want.

Perversely the cause of not being able to refactor has nothing to do with tech debt and cannot be solved in this way. It's multifaceted so at different places it emerges in different ways, but usually it's an incentive problem. At some places it is that the only incentives are for feature development. At other places it is that every developer is working on a different thing rather than prioritizing one thing that the business needs and delivering it, and allowing developers to use their slack time in this equation to improve the product however they see fit... In yet another it is because the team lead resists any suggestion that the framework being used is too heavyweight for the problem being solved and so trying to keep these very clean abstraction layers is causing people to have to rewrite the same basic thing in five different unrelated places, because it has to bubble up from the data layer into the service layer into the business layer into the controller layer into the API into the consumer layer into the app state layer into the frontend model layer...

3 comments

> Code doesn't deteriorate... Like we're talking about a banana growing spots or whatever...

It kind of does -- if you leave a codebase alone for a long time, and you come back to it later to upgrade a lot of dependencies (sometimes making a multi-version jump), it's a lot harder than it would have been to keep them updated as new versions were released.

It would have been a lot worse if that log4j CVE had been in a library with a lot more transitive dependencies or makes breaking changes between versions, like Jersey.

One advantage of monorepos with shared dependencies is that even the parts of the code that don't need to be touched very often will still get the latest dependency updates. If those codebases are in standalone repos, they just sit there, and then one day a simple attempt to upgrade a dependency turns into hours of work.

So, it's not technically "rotting," but it's definitely the case that leaving code to sit creates more technical debt later on -- even if that code was perfectly good the last time anyone worked on it.

Can you share where you found this definition of technical debt? I've honestly never heard it articulated this way before.
> Another, more serious pitfall is the failure to consolidate. Although immature code may work fine and be completely acceptable to the customer, excess quantities will make a program unmasterable, leading to extreme specialization of programmers and finally an inflexible product. Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite. Objects make the cost of this transaction tolerable. The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object- oriented or otherwise.

http://c2.com/doc/oopsla92.html

Technical debt is only "positive" in the sense that it may permit shipping something now or earlier, but it accumulates and becomes something that can slow the project team down to a crawl, or worse totally stall forward progress. In the end, unless your project can be thrown away, it's a negative.

By the same token if you ignore all of the good parts about having a credit card, “in the end, unless your credit card can be thrown away, credit card debt is a negative.” There's a valid perspective for this but it's not a great one.

It's not just that it permits something now or earlier, but in shipping something now or earlier it can also increase the quality of what you are shipping. Ward saw it as the center of his preferred style of coordinated software development, XP. Sort of the old Daoist idea that the wheel needs the hole at its center, the negative enables the positive.

Original sources by Ward Cunningham are not too hard to come by...

> I became interested in the way metaphors influence how we think, after reading George Lakoff and Mark Johnson's Metaphors We Live By. An important idea is that we reason by analogy with the metaphors that have entered our language.

> I coined the debt metaphor to explain the refactoring that we were doing on the WyCash product. This was an early product done in DigiTalk Smalltalk, and it was important to me that we accumulate the learnings we did about the application over time by modifying the program to to look as if we had known what we're doing all along, and to look as if it had been easy to do in Smalltalk.

> The explanation I gave to my boss, and this was financial software, was a financial analogy I called the debt metaphor. And that said that, if we fail to make our program align with what we then understood to be the proper way to think, uh, about our financial objects—then we were going to continually stumble over that disagreement: and that would slow us down, which is like paying interest on a loan! With borrowed money, you can do something sooner than you might otherwise, but then until you pay back that money, you'll be paying interest.

> I, uh, I thought borrowing money was a good idea, I thought that rushing software out the door to get some experience with it was a good idea. But that of course you would eventually go back, and as you learn things about that software, you would repay that loan by refactoring the program to reflect your experience, as you acquired it.

> I think that there were plenty of cases where people would “rush software out the door,” and then learn things, but never put that learning back into the program. And that, by analogy, was borrowing money thinking that you never had to pay it back. Of course, if you do that, say with your credit card, eventually all your income goes to interest, and your purchasing power goes to zero. By the same token, if you develop a program for a long period of time by only adding features and never reorganizing it to reflect your understanding of those features, then eventually that program simply does not contain any understanding and all efforts to work on it to take longer and longer. In other words the interest is total—you'll make zero progress!

> A lot of bloggers at least have explained the debt metaphor and uh, confused it, I think, with the idea that you could write code poorly with the intention of doing a good job later... and thinking that that was the primary source of debt. I'm never in favor of writing code poorly, but I am in favor of writing code to reflect your current understanding of a problem, even if that understanding is partial.

> You know, if you want to be able to go into debt that way, by developing software that you don't completely understand, you're wise to make that software reflect your understanding as best you can: so that when it does come time to refactor it's clear what you were thinking when you wrote it, making it easier to refactor it into what your current thinking is now. In other words, the whole debt metaphor, or let's say the ability to pay back debt and make the debt metaphor work for your advantage, depends upon you writing code that is clean enough to be able to refactor as you come to understand your problem. I think that's a good methodology, it's at the heart of extreme programming... The debt metaphor is one of many explanations of why extreme programming works.

—Ward Cunningham, https://youtu.be/pqeJFYwnkjE

> Code doesn't deteriorate

Your specific codebase may not, but the frameworks, OS, and hardware it's running on evolves and changes overtime. Software practices change over time as we find more effective ways (hopefully) to do things. If you expect to let code sit and come back to it in 10 years and think that everything will be hunky-dory, I've got a bridge to sell you.

> It is the mismatch between our domain model and how our users think about the domain.

...but the business domain continues to evolve. A new client comes in, another software system is onboarded, the organization is restructured, new regulations are inacted that need compliance. I think the idea that technical debt only exists because we didn't "perfectly match" the domain and our model at the start is a bit short sighted.

> But at some point it started to mean that we had to upgrade our dependencies, that's tech debt, or this kludge that I threw in, that's tech debt

The broadest definition of technical debt that I've seen boils down to technical challenges that impede developer progress that does not have direct concern to the business. One of the reasons that I think that it frames a purely technical issue (i.e how the debt got there and how it needs to be resolved) with something that the business wants (rapid development). While there are a multitude of challenges in resolving technical debt, a significant one is getting the business to allocate time to resolve it. In order to do so, there needs to be some value for the business, not just the developers.

>More broadly, anything that we no longer care for is tech debt. And that's where you really get this idea that it is deteriorating, that's more a measure of our own patience deteriorating, especially as we never seem to have time for the refactors we want.

If you're defining technical debt as "anything you don't like", then you don't have a real understanding of your technical debt. One of the key pieces in the broad definition above is that the debt "impedes developer progress". Old code bases have plenty of things that modern developers don't like, but it works and it doesn't impact the application overall. It's old, but we barely touch it, so it's age or style is of little consequence. If you don't know what technical issues are causing signficant issues for your team or your codebase, then there's little justification for "paying it off". If you can sell the business on it, then you tend to get into long refactoring projects that provide little value than inflating developer egos.