Technical Debt
Technical debt is a concept that concerns engineers, studied in project management certifications, and relevant for product managers to understand when developing product strategies. It's a problem whose cause usually lies with programmers and other involved departments.
Everyone views technical debt as something that must be eliminated. The truth is that it's a natural phenomenon that everyone must accept as it's part of life's reality. Anything shared by more than one person will inevitably develop problems. It's unavoidable and will appear in every project you work on.
Why Does Technical Debt Exist in the First Place ? Different Perspective
As mentioned earlier, technical debt is inevitable, everyone must accept it, and it will always exist. The simple reason is the concept of "contribution", when multiple programmers contribute to developing a technical product, this in itself is a fundamental cause of technical debt.
Imagine sharing something you own with other people. The likelihood of that thing getting damaged or lost increases with the number of people sharing it with you. Conversely, if you were the sole owner, the probability of damage or loss could reach zero!
Of course, there are other contributing factors that worsen the problem, such as poor hiring quality and weak leadership. If the project leader lacks strong technical skills or the leadership abilities needed to stop technical debt before it occurs, or if the team quality is poor, it will significantly contribute to code base deterioration and cause technical debt that becomes impossible to resolve.
The work environment also plays a major role. A programmer is ultimately an employee who needs incentives to perform. Without an encouraging and supportive work environment, you end up with an employee who performs their required tasks with no more and no less. You cannot blame the employee for technical debt at all, because it's something you cannot measure. You cannot say that this part of the code contains technical debt and this employee contributed to worsening the problem by X percentage “it's impossible”. So the employee ends up completing the task, but in the long run, the company bears these burdens.
Is It the Engineer Problem or the Leader's Responsibility ?
It is everyone's responsibility, without a doubt. However, in my opinion, the final blame lies with the team leader and the work environment. They should bear all the blame because they are people who were granted authorities (hiring, firing, approvals, orders, etc.). Bad results are always caused by poor use of authority. If an employee caused technical debt, the reason for their presence in the first place is the leader who hired them, or the work environment that may contribute to reduced productivity.
No matter how hard you try to instill the concept of ownership in an employee, the environment must reflect this principle. No employee will adopt this concept without an encouraging work environment or a leader who empowers their team. The roots of technical debt (and many other problems) are human in the first place.
Preventing Technical Debt Is Better and More Efficient Than Trying to Remove It
One of the leader's responsibilities, in my opinion, is not just removing technical debt, but preventing it from the start! This requires many combined skills (technical and leadership). Your technical skills help you detect flaws in any proposal and their impact on the code base and the leadership skills help you in addressing them.
Preventing technical debt doesn't have to be limited to programmers only. "Nice to have" features are also a cause of technical debt, and here the leader must try to educate other departments about the severity of adding features to the product without real value. Technical product development isn't just about adding features; product development can be achieved by removing steps that increase user journey complexity. These concepts may be absent from product managers, so it's essential for you as a leader to share these ideas and defend the code base from random additions. Adding new features must be based on numbers and facts.
Also, your role as a leader in preventing technical debt is through reviewing code. You must stop monitoring linting issues and other problems that don't affect the product's technical quality and can be automated. Your focus in review must be at a higher level: transferring knowledge to the team by suggesting improvements on API specs, reducing complexity of certain functions, and other observations that prevent technical debt and help transfer knowledge to the team.
Accepting technical debt from the start and then working hard to remove it later affects the quality of your time and is a waste of a very important resource that you could invest in solving real problems.
Why Do You Need to Remove Technical Debt ?
First, let's agree on an important point: exerting effort to remove all technical debt is considered a waste. Refactoring may seem enjoyable and give you a false sense of achievement, while the modifications you make have minimal impact. From here comes another question: "When is removing technical debt useful ?" (More on this later). Here we'll talk about the benefits of removing technical debt that requires removal.
Technical debt causes you many problems, which I'd like to summarize into two that significantly affect product development. Imagine you have a feature with high technical debt. You'll face two problems with this feature in the future if you plan to:
- •Develop this feature further
- •Fix problems found in it
From here, I recall two features I worked on that had the most technical debt problems I've faced. The first feature was a report generator with complex details, requiring displaying information for many users and performing complex calculations. this feature was built wrong, and that's not even the biggest problem. This feature was in its early stages and was constantly requested to be modified. Over time and with frequent modifications, reading the code became extremely difficult, a nightmare in every sense. So I would simply, if asked to add something to the report, add the required part directly, even though modifying the code might have been better. But I was in an environment that was hard to understand, and I was afraid of problems arising from modifying something that was working. After all this, problems started appearing, and the debugging process would take hours.
The second feature I worked on was a date picker. At the time, I was using a component library that provided several reusable interfaces with custom theming, but unfortunately they didn't provide a date picker at the time. So I thought to myself, I'll build it myself, it's better because the requirements don't have complexities yet (the user doesn't need to select a range or more than one date). Of course, I underestimated building a very complex component, and over time problems started appearing that I hadn't accounted for, like displaying the date picker on different devices, if the date picker opened at the bottom of the page, and other problems, not to mention requests for additions to this component like having multiple variants and accepting dates in both past and present. All these problems had already been solved and tested by brilliant people, but I took a difficult path and placed a very high maintenance burden on myself.
Technical debt hinders you, and you must identify the places where technical debt is consuming high resources and draining the team's energy. Among the returns of removing technical debt: understanding the code in the future will be much easier (instead of trying to read the code multiple times to understand it), and therefore you can modify with high confidence. Also, many problems that were hidden in the old code will completely disappear because you treated the root of the problem, so code problems will decrease.
There's also an important point: when you try to remove technical debt, your history with this code will greatly help you in making decisions customized specifically for this feature. Based on this, you'll come up with a complete solution, so don't underestimate this matter even if it requires weeks from you. The ROI is huge!
How Do You Remove It ? And Does All Technical Debt Need to Be Removed ?
Not all technical debt needs to be removed. Every time you feel the urge to remove technical debt, ask yourself this question: "When will this technical debt cause me a real problem ?" Some features exist in the product but their usage is very limited and in preliminary steps, not constituting the user's daily usage. Problems may occur with this feature and no customer notices until a new customer comes. Or some features in the product with few users actually use it. Here, the return on investment in removing technical debt is very small, and solving this feature's problems directly may be more efficient than rewriting it.
Also, technical debt shouldn't be removed directly. I remember once we requested from management a "break 😅" from any new requests so we could focus on removing technical debt. This method is impractical, not just because you stop development, but because removing technical debt is very difficult and takes a long time, especially for technical debt that requires restructuring (data migration which is a dangerous process, removing parts used by other parts without technical debt, etc.). Our future solution to the problem was to have two versions of the same feature: a version under development and a legacy version running in production, using a feature flag so you could switch to either version whenever you felt ready. This process would take months, but it was very practical because we were able to launch new features with complete confidence and without problems.