TECHNICAL DEBT IS NOT ALWAYS A STORY ABOUT BAD DEVELOPERS
There is a convenient way to talk about old software. Call it legacy. Talk about technical debt. Open the code. Look concerned. Then explain that the previous team made poor architectural decisions and the system now needs modernization.
I have a problem with that story.
Sometimes I know exactly who built the old system. We did. I know why some of those decisions were made. I remember the discussions. I remember the marketing requests. I remember looking at user behavior and moving elements because the data told us to. I remember the product changing.
Many of the decisions were reasonable at the time. That does not mean they remain reasonable forever.
Martin Fowler describes technical debt through the extra effort required to modify and extend a system because of its internal quality. He has also made an important distinction that is often lost in sales conversations about legacy software: some debt is not reckless or even deliberate. It can be inadvertent and still emerge in the work of excellent teams.
That distinction matters to me.
Because a product that has been actively developed for five years has a history. A button was moved for a reason. A condition was added for a reason. An exception existed because one product category behaved differently. A field appeared because, at that moment, somebody needed that data. An integration was written around the system that existed then.
The product keeps moving.
The code remembers. And after enough years, the code may remember more versions of the business than the current team does.
A GOOD DEVELOPER CANNOT JUST DELETE WHAT LOOKS OLD
This is where the cost becomes real.
A good specialist opens the code and finds an old condition. It looks unnecessary. Maybe it is unnecessary. But a professional developer cannot responsibly say: “I don't understand this, so let's delete it.”
The fact that I do not understand why something exists is not evidence that it can be removed.
First, someone has to investigate. Why was this added? What depended on it? Does anything still depend on it? Was it connected to a specific category, a campaign, a product status, an old integration? Does the code still execute? Does the business still use the process? Is the feature dead, or does one person in the company use it twice a month and nobody else remembers?
So the developer starts reading. Checking history. Tracing dependencies. Looking at data. Testing behavior. Talking to people. Sometimes talking to people who worked on the project years ago.
And very often the answer is: “I think we don't need it anymore.”
Think. That is the problem.
You cannot build responsible software maintenance around I think. The work begins before the change begins. And that work costs money.
There is a long history of software engineering research around program comprehension because understanding an existing system is a major part of maintaining and evolving it. More recent work is beginning to separate technical debt in code from what researchers call cognitive debt and intent debt, the erosion of shared understanding and the loss of captured rationale behind previous decisions.
I did not use those terms when we were working on these projects.
My version was simpler: why are we spending so much time remembering what we already built?
SOMETIMES THE SYSTEM IS OLD BECAUSE THE PRODUCT WAS ALIVE
One of our long-term e-commerce projects has been running for more than five years.
The original system was built on an open-source CMS. At the time, it was the right decision. The business needed an e-commerce platform. B2C was the primary direction. B2B existed, but it was a relatively small part of the product. So the system reflected that.
Then we worked on it. A lot. We analyzed behavior. We looked at heatmaps. We changed product pages. We changed the catalog. We worked with the interface. We moved elements. We added logic. We connected services.
The business kept operating, and the product kept responding to the business.
This is important. The system was not abandoned for five years. It was alive for five years.
An abandoned system becomes old because nobody touches it. A living system can become complicated because everybody keeps touching it for good reasons.
The distinction is easy to miss when you look only at the final codebase. You see conditions, old styles, rules, exceptions, things that appear to duplicate other things. But you do not see the meetings. You do not see the heatmap from three years ago. You do not see the campaign that changed the product page. You do not see why one category needed an exception for six months.
The code is still there. The context is gone.
Sometimes technical debt is not evidence that nobody cared about the product. It is evidence that people cared about it for years.
THEN THE BUSINESS CHANGED SHAPE
Technical debt alone did not make us decide to rebuild the system. That would be too simple.
Something else happened. The business itself changed.
Years ago, B2C was the main direction and B2B was a smaller part of the site. Now the client needed to divide those directions almost equally.
That sounds like a website task. Add a B2B section. Create more pages. Change the navigation. We could do that.
But B2B and B2C were no longer two sections of the same conversation. They had different tasks, different positioning, different customer journeys, different information requirements. The business had divided into two significant directions.
The software still believed one of them was a section.
At the same time, product filtering had become substantially more complex. The catalog needed structural changes. The product page needed to be reconsidered. Search needed a different approach. ERP and CRM integrations had become more important. The business had diversified. New products were being developed around it.
And we were already asking questions about new AI interfaces and how the product and catalog should work in a different search environment. Some of those questions simply did not exist when we designed the original system.
The old CMS had not failed. The question was not even whether we could continue modifying it. We could.
The question was how much of the old system we were now rebuilding while pretending we were still modifying it.
YOU MAY ALREADY BE REBUILDING THE SYSTEM INSIDE THE OLD ONE
This happens gradually. Nobody schedules a meeting and says: “Starting Monday, we will spend the next two years rebuilding our platform inside the old architecture.”
You just approve another task. The product page needs a structural redesign. Fine. Then the catalog. Then filters. Search. A new integration. A different B2B flow. New product logic. Another part of the business no longer fits the old data structure.
Each task can be estimated separately. Each task can be justified separately. Each task may even make sense separately. And because each individual modification is cheaper than a complete rebuild, the company keeps modifying.
But at some point, you have to stop looking at the tickets one by one. Look at the direction of the work.
If we are redesigning the product page, catalog, search, filters, integrations and customer journeys, what exactly are we preserving? The domain? The old database structure? Years of conditions we are afraid to remove? The name of the CMS in the administration panel?
Sometimes you are already rebuilding the system. You are just doing it inside the old one.
This is where a simple comparison can become misleading. The next modification may cost less than a rebuild. Of course it does. The question is not whether Task #1847 is cheaper than a new platform.
The question is what happens when the next twenty important changes all begin with the same process: first, we need to understand what is already there. That is a different cost.
THE RECURRING COST OF UNDERSTANDING
I think this part is underestimated.
People understand maintenance cost as development hours. Change the feature. Fix the bug. Update the integration.
But in a mature system, a significant part of the work can happen before a developer is allowed to make the change responsibly.
A good specialist needs time to understand the system. CI can help. Tests can help. Version history can help. Documentation can help. AI can increasingly help teams read and summarize unfamiliar code. All of that matters.
But none of it automatically answers the business question: does the reason this code was written still exist?
The code can tell us what a condition does. The commit may tell us when it appeared. A test can tell us what behavior will break if we remove it. That still does not always tell us whether the company needs that behavior in 2026.
Someone has to connect the old implementation to the current business.
That is why I think technical debt creates a cost that is easy to miss in estimates. It creates a recurring cost of understanding the system before you are allowed to change it. And sometimes you pay that cost more than once.
A developer studies the catalog logic. Six months later, another specialist works on search and traces part of the same history from another direction. A year later, the product page changes and the team returns to old conditions again.
The system may still work. But more and more development time is being spent not on designing the next version of the product, but on understanding why previous versions looked the way they did.
Recent discussion around software health has started naming this problem more explicitly. Margaret-Anne Storey's 2026 paper distinguishes technical debt from cognitive debt and intent debt: the code may remain, while shared understanding of the system and the rationale behind decisions erode. Storey frames the distinction succinctly herself: technical debt makes systems harder to change, while cognitive debt makes systems harder to understand.
That is very close to what I have watched happen in practice.
The old code is not always the whole problem. Sometimes the expensive part is that the old code keeps asking the team to remember a business that no longer exists in the same form.
REBUILD IS NOT A PUNISHMENT FOR THE OLD SYSTEM
This is why I do not like the usual language around legacy modernization. The old platform is holding you back. The previous architecture was wrong. The CMS cannot scale. The legacy system failed.
Sometimes those statements are true. But they are not always true.
In our case, the original system worked for years. It supported the business. It changed with the product. It gave us data. It gave us user behavior. It helped the company reach the point where B2B and B2C could become different directions.
Why would I call that a failure?
We are not rebuilding the system because the old decision was stupid. We are rebuilding because the conditions around that decision changed.
The business changed shape. The requirements changed. The product accumulated history. And the cost of adapting the old structure began to compete with the cost of designing around the business that actually exists now.
That is a very different conversation.
A rebuild does not prove the old system was wrong. Sometimes it proves the old system worked long enough to reach the end of the problem it was designed to solve.
THE HARD PART IS KNOWING WHEN
I wish there were a clean formula. When technical debt reaches 37%, rebuild. When developers spend 14 hours understanding old logic, rebuild. When the CMS has 62 modules, rebuild.
There is no serious answer like that. And I would be suspicious of anyone who gives one before studying the product.
A five-year-old system can be perfectly reasonable to continue developing. A two-year-old system can already be structurally wrong for the business. Old code is not automatically bad code. Technical debt is not automatically a reason to start over. A growing business is not automatically a custom software project.
You have to look at the whole direction. How much of the product now needs structural change? How much time does the team spend understanding old logic before making new changes? How many current requirements are being forced into assumptions made for an earlier version of the business? Are integrations becoming deeper? Has the product model changed? Has the customer journey changed? Has the company itself divided into new directions?
And when you look at the next two or three years of planned work, are you really extending the existing system? Or are you slowly replacing every important part of it one ticket at a time?
For me, that is the uncomfortable question. At what point are we spending more time understanding the old system than designing the next one?
Not every product reaches that point. This one did.
THE CODE REMEMBERS EVERY VERSION OF THE BUSINESS
I sometimes open an old project and recognize my own decisions. I know why we moved that element. I remember why the product page changed. I remember some of the logic. Other parts take time. Some decisions I no longer remember at all.
That is normal. Five years is a long time for a living digital product. People change. Teams change. Marketing changes. Customers change. The business changes.
The system keeps the artifacts. A style. A condition. An exception. A field. An integration. One button carrying five years of decisions.
There is a point where cleaning those decisions one by one still makes sense. There is also a point where the next version of the business deserves to be designed from the current reality rather than reconstructed through the accumulated logic of the previous one.
The difficult part is admitting when you have reached it. Especially if the old system still works. Especially if you built it.
Maybe especially then.
The old system is full of decisions that used to make sense. That is exactly why you have to understand them before deciding whether to keep building on top of them.
And sometimes, after you understand them, the responsible decision is not to add the next layer. It is to start the next system.
Read also
-
17. 07. 2026
The Build vs. Buy Equation Has Changed. Most Companies Have Not Recalculated It.
-
12. 03. 2021
In-House vs Outsourcing in 2026: What Business Owners Actually Need to Know
-
18. 06. 2026
How to Hire a Remote Dev Team Without Getting Burned
-
15. 07. 2026
Everything You Build Starts Aging the Day You Finish It