What are intelligent strategies to keep technical debt at bay?
Technical debt is a hard problem. But that kind of problems has sometimes surprising solutions, where one leaves a local optimum to find a better optimum point.
What kind of smart strategies did you find to reduce technical debt in relatively new code bases?
As an aside, in the last time, I have heard several times that using new programming languages / language standards like C++20 or Python 3.10 helps against technical debt. I am not so sure about that one. Sure, code bases with new language versions or standards are overall newer, and therefore, they tend to have less technical debt. This is quite logical to me. But my impression is that good practice and experience is what makes the crucial difference seven of eight years later. In fact, I suspect that languages that are evolving rapidly might accumulate more technical debt, as they are less stable by concept.
All this is superimposed with business fields where few codebases are older than some three years (like typical start-ups) and others where a vast amount of code runs 20 years or more (like industrial automation and banking). And there are even some communities which are using rather old languages because of their (real or perceived) stability.
Technical debt is an explicit choice to optimise for speed of delivery at the cost of having to do more work later to bring the delivered code up to the usual expectations of code/features in the system. Ideally the choice to take on that technical debt comes with an agreement of when the debt will be paid down and an understanding of the additional work that will be required because of the shortcut being taken now.
It is not technical debt if there is no explicit decision, or it is the gradual and continuous bitrot that all codebases suffer. I usually refer to this as cruft and while good design and writing code to optimise for readability / understandability reduce the rate that cruft accumulates, it cannot be stopped due to the nature of software. The best practice I have found to fight cruft is many small refactorings and cleanups as part of every piece of work I deliver. This strategy is outlined in https://ronjeffries.com/xprog/articles/refactoring-not-on-th....
- Start by making sure the ones committing Code to are also the ones getting called at 2 AM if there is a Production problem.
- Get metrics the reward teams with Stable and Reliable systems, and avoid metrics that just reward teams that commit the most features
- Make sure the code reviewers have skin in the game, and that what they allow to pass reviews is also theirs to fix, not the the original coder.
For the love of God, critically think about your database schema and object model at a very detailed level, taking into account likely future requirements. I can’t emphasize enough how important having the correct data shape is from the beginning. This is something you cannot ‘iterate on.’ You have to nail it from the get go, especially the parts central to your application. It’s either right or it isn’t and as soon as it is baked throughout your entire codebase it is too late to change.
I am currently refactoring a giant system that had a badly normalized object model that couldn’t be extended and have been doing so for over a year.
Keeping it simple, stupid. I'm writing a database for example and go out of my way to use conceptually simpler algorithms, even if they are slower. I use optional patches to refactor them.
Ideally simplicity leads to adaptibility, simple things make less assumptions, and maintainability, it's easier to understand the behavior of simple things. That being said simplicity for it's own sake is not important, adaptibility and maintainability is what's important.
Using a language that changes often and have a lot of subtlety or features, especially macros hurts maintainability, because one needs to understand the language first.
Be careful not to confuse technical debt with technical entropy.
Technical debt may be nothing more than a prioritization strategy that is either intentionally or unintentionally blindsided by a technical asymmetry of attention to a rate of system degradation.
Smart organizations will throttle the degree to which development performs like an ever insatiable desire to maximize efficiency at the cost of mortgaging away the responsibility for system refreshment.
Just as "fitness functions" are desirable sensors for expiring licensing and contractual renewals so should an enterprise think about and implement ecosystem fitness functions that compare existing system state against the latest industry trajectories. Identifying, recognizing, and rectifying business ecosystem weaknesses will go a long way in preventative maintenance and reduced technical entropy and debt.
Iterate. Don't do energetic and expensive sprints where you are in 10x mode for long periods (to avoid burnout). Attack the pertinent stuff first. Batch all the easy stuff into one large task, but again: avoid a sprint tackling that.
Watch Mike Rother's Kata videos on YouTube. Every org is a snowflake. What works for one will not work for others. You need scientific thinking and practice to find what works for your snowflake.
Technical debt is a vague and very ill-defined term that can be used by both engineers and managers to cover for dysfunctional organizational dynamics. So to avoid it requires a functioning organization whose members are committed to technical excellence. Short of that there is no reasonable way to avoid technical debt because it is fundamentally a reflection of the organization and not the code.
undefined
[dead]