When trade-offs, debt and legacy collide
Every architectural decision has a cost. Some costs are accepted, some are postponed, and some appear because evolution never happened. Understanding these differences can change how you assess and improve a system.
During my career, I’ve had the opportunity to talk with hundreds of architects, engineers, and leaders about their systems. Regardless of my role, I was usually there to help them improve the architecture of an existing solution. In most cases, we were discussing long-lived applications. What has always intrigued me is how many brilliant engineers treat every smelly line of code, every imperfect boundary, or every design choice that could be improved as technical debt.
To be honest, your current architecture is rarely pure technical debt. There are other factors that contribute to the overall cost of the modernization you want to undertake. While this distinction may initially sound theoretical, it is worth understanding the differences and recognizing what led to each problem in your system.
Such understanding will not only help you modernize the application more effectively, but it will also reduce the risk of ending up in the same situation again. Just as importantly, it can reduce the mental burden that often accompanies large-scale modernization efforts.
Every Decision Has a Cost
When we look at a software system at a specific moment in time, the picture can sometimes be terrifying. If that is the case in your situation, there is a good chance you have asked yourself: How could anyone let this happen? Who made the decisions that led to this state?
I am quite sure you are neither the first nor the last person to think that about a system that requires modernization.
The challenge is that we see only the current snapshot, with all its problems, limitations, and accumulated complexity. What we do not see is the context in which those decisions were made. No engineer or architect intentionally made a choice knowing that its outcome would become your future problem. When those decisions were made, the situation looked different. There were constraints that had to be considered, many of which may no longer exist today. Most likely, the chosen solution was perfectly reasonable at that moment.
And yet, here we are. Months, years, or sometimes even decades later, it becomes difficult to understand what happened along the way.
First, it is worth remembering that every decision has a cost. The perfect choice does not exist. Every option comes with consequences that we must accept. In many cases, the bigger the problem we solve, the greater the cost we introduce.
Second, decisions do not exist in isolation. Every choice affects decisions made in the past. Sometimes that impact is positive, and a new decision compensates for previous costs or amplifies existing benefits. In other situations, a new choice increases existing risks or reduces the value created by earlier decisions.
Third, and equally important, not every decision is made consciously. Sometimes we simply follow an existing pattern or adopt a standard solution. Sometimes we do not have enough time to think. And sometimes we do not even realize that deeper consideration is required.
It is important to understand that a system evolves and that all these factors influence its trajectory. Not because we can completely prevent them - it is difficult, if not impossible - but because awareness helps us understand how costs change over time and how they accumulate. This perspective also explains why it is worth revisiting architecture regularly. It is much easier to make improvements when you notice the first symptoms rather than when you are already facing serious problems.
Cost May Have Many Faces
When people talk about software architecture, technical debt is often presented as the primary reason for modernization. Personally, however, I believe technical debt is not the main source of architectural problems in most situations.
Why?
Because there are several different types of costs we should consider:
- Trade-off - the cost of making a decision.
- Technical debt - the cost of the gap between the current state and the desired state.
- Legacy - the silent cost of evolution that never happened.
Trade-off
A trade-off is the cost we accept when making a conscious decision and deliberately choosing one option over another.
For example, we may need to decide whether a system should be built as a monolith or as a set of microservices. Both approaches have advantages and disadvantages. There is no perfect choice, only the one that best fits the current context.
If we choose microservices, we accept greater infrastructure complexity in exchange for the benefits that microservices can provide. That complexity is not a mistake, nor is it something we necessarily plan to remove later. It is part of the solution itself. We accept that cost because, based on the information available at the time, it is the most suitable choice.
Trade-offs are common in software architecture, and they generate a significant portion of the costs we carry. However, this is a cost we willingly accept because of the value the decision enables.
Technical Debt
Technical debt is the cost of a shortcut we consciously decide to take.
Just like a trade-off, it is the result of an intentional architectural decision. We are aware of the problem, we evaluate alternatives, and we make a deliberate choice. The difference is that we knowingly choose a solution that is not suitable in the long term. From the very beginning, we assume that it will eventually need to be changed.
Perhaps we skip a proper design because of a deadline. Perhaps we implement a temporary solution because the business needs immediate results.
In both cases, we understand that the constraints of the chosen approach will eventually become a problem. This is the key distinction between trade-offs and technical debt.
Both result from intentional decisions, and both involve accepting a cost. However, when we agree on a trade-off, we do not expect the decision itself to change. With technical debt, we know from the start that it must be addressed in the future.
Legacy
Legacy is different. It is a cost we are often unaware of until it becomes large enough to hurt us.
To put it simply, trade-offs and technical debt are the result of decisions we made. Legacy is the result of decisions that should have been made but never were.
There can be many reasons for this.
Perhaps we underestimated the complexity of a problem and started implementation without considering its architectural consequences. Perhaps we relied on familiar solutions because of time pressure. Perhaps the cumulative effect of many architectural decisions created a new cost that emerged only from their specific combination.
Legacy appears when we ignore the fact that architecture evolves and should be revisited. Or when we simply do not realize that it should.
The Type Can Change
To make things even more interesting, costs can change their nature as the system evolves.
- A past trade-off can become technical debt when circumstances change or new constraints emerge.
- Technical debt can effectively become a trade-off if our assumptions prove incorrect or the system evolves in an unexpected direction.
- Both trade-offs and technical debt can generate legacy when multiple decisions combine to create unexpected and unnoticed consequences.
Why It Is Worth Differentiating the Costs
Recognizing different types of costs helps determine how they should be managed.
Trade-offs
Trade-offs should be revisited periodically.
No action is required as long as the assumptions behind the decision remain valid. This is why reviewing architectural decisions is important. If you notice a change in context early enough, you gain more time to adapt before the cost starts increasing.
Technical Debt
Technical debt should be managed from the moment it is created.
For example, you can agree on a date when investment in a long-term solution will begin, or define measurable thresholds that trigger action.
Technical debt always implies future work. Tracking it makes it much less likely that the shortcut will simply be forgotten.
Legacy
Legacy is the most challenging category because it emerges where "things just happen."
What can you do about it? Monitor the system.
Observe trends such as deployment frequency, lead time, change failure rate, or the pace of development. You can use DORA metrics for this purpose. When you notice that these indicators are consistently moving in the wrong direction it is worth reviewing the architecture to understand what is causing the trend.
How to Use This Knowledge to Avoid Repeating the Same Mistakes
These categories can also help you assess the overall health of a system.
Trade-offs
Were important architectural decisions documented, for example using Architecture Decision Records (ADRs)?
If yes, great - you already have valuable information about how the architecture evolved over time. If the documentation does not exist, is incomplete, or is misleading, introducing ADRs should become a priority.
You should also ask whether these decisions were reviewed periodically. Not whether new records were continuously added - that is a natural consequence of maintaining a system - but whether existing decisions were revisited to validate that new circumstances had not invalidated previous trade-offs.
If that never happened, a modernization initiative is a good opportunity to introduce regular reviews, for example once per quarter.
Technical Debt
As with trade-offs, documentation matters. You should verify whether technical debt is tracked, prioritized, and discussed in relation to risks and opportunities.
The mere existence of a technical debt backlog is not enough. I have seen many "dead" technical backlogs where items were continuously added but never addressed. Such backlogs create nothing more than an illusion of debt management. In practice, they are no different from having no backlog at all.
If there is no mechanism for tracking technical debt, creating one should be among the first improvement initiatives. Sometimes making the problem visible is exactly what was missing.
Legacy
Is the system monitored?
Is Sonar integrated?
Is there a CI/CD pipeline?
Are DORA metrics being collected?
If the answer is no, start there.
It is difficult to discuss quality improvements when there is no measurement. Without metrics, every modernization effort becomes little more than an educated guess.
Summary
As you can see, the current cost of a system is the result of many different choices- some intentional, some accidental. Some of those choices were the best available options at the time. Others were compromises forced by constraints and circumstances. All of them influence the architecture, and all of them deserve attention.
Understanding the difference between trade-offs, technical debt, and legacy allows you to choose the right strategy for each situation. More importantly, it helps you understand why your system looks the way it does today and how to prevent the same problems from reappearing tomorrow.
Comments ()