DEV Community

Mahesh Gupta
Mahesh Gupta

Posted on Originally published at thedebugledger.hashnode.dev on

Your Legacy System Is Not Technical Debt Just Because It Is Old

On paper, one of the easiest numbers in a system conversion should be the total premium.

In practice, it was one of the numbers that could stop us.

Around 2010, I was an Oracle Tech Lead on a project at The Doctors Company that was converting legacy policy and claims data from a system called STAR into a newer system called NOVA. Part of my work involved reconciliation. We compared converted results across premium totals, policy counts, insureds, coverages, endorsements, and other insurance data.

When NOVA produced a premium total that did not agree with STAR, the fact that NOVA was the newer system settled nothing.

From the business side, STAR was the baseline. If STAR said one thing and NOVA said another, the immediate problem was the difference. Business and management stakeholders became involved, and we often had to move below aggregate totals into individual policies, coverages, and endorsements to understand what was happening.

That experience changed how I think about legacy modernization.

Years of accepted production behavior gave STAR a practical authority that NOVA had not yet earned. Any difference therefore needed an explanation before the new result could be trusted.

I have seen the word legacy used almost as a diagnosis. The application is fifteen years old. The database version looks dated. The user interface belongs to another era. Therefore the system must be technical debt, and modernization must mean replacing it.

I think that skips the most important question.

What problem is the age of the system actually causing?

New software does not get a presumption of correctness

Consider a simplified version of the reconciliation problem:

STAR premium total = X
NOVA premium total = Y

Enter fullscreen mode Exit fullscreen mode

If X != Y, an engineer cannot settle the issue by saying that NOVA is newer, so Y must be correct. The business cannot settle it by saying that STAR has always produced X, so X must be correct either. Someone has to explain the difference.

That sounds obvious when reduced to two variables. It becomes much harder when the difference is distributed across thousands of policies, rating attributes, coverages, endorsements, conversions, and historical records.

A replacement system creates a burden of proof.

It has to demonstrate that the business meaning survived the transition, or that any deliberate change in that meaning is understood and accepted. A successful load, matching record counts, and a cleaner data model still do not establish that the converted system behaves correctly. The resulting business behavior has to reconcile.

Modern migration guidance also emphasizes schema mismatches, data integrity, and validation, including the risk of treating a technically successful transfer as a correct business conversion.

The real work began at the point of disagreement: tracing the discrepancy until the underlying rule, data condition, or transformation was clear enough to defend.

The business history embedded in old systems

Over years of use, enterprise applications become repositories of accumulated business choices, exceptions, dependencies, and operating assumptions. Some are obvious: rating formulas, eligibility rules, financial calculations, workflow steps, validations, approval paths, reporting definitions, and integration contracts. Others are less visible. A particular field is populated in a strange way because another system expects it. A batch has to run in a certain order because a downstream report assumes the earlier result exists. A condition that looks redundant may represent an exception created years earlier after a production incident.

Not every historical behavior deserves to survive modernization. Some old logic really is obsolete. Some workarounds should be removed. Some data should be archived rather than migrated. Some customizations exist only because the old platform could not support a cleaner approach.

But before deciding that a behavior is obsolete, someone has to understand what it does. That is where the phrase "rewrite it from scratch" makes me nervous.

A rewrite gives you a clean codebase. It does not give you a clean history.

The undocumented requirement does not disappear because the old stored procedure disappears. The replacement team has to rediscover the requirement, decide whether it still matters, and then either reproduce it or consciously retire it. In that sense, some ugly code is not merely a maintenance problem. It is business archaeology.

Age is a poor modernization metric

Software obviously ages over time. Vendors stop supporting versions. Security expectations change. Integration patterns change. Hiring becomes harder for some technologies. Transaction volumes grow. Business users expect capabilities that were never imagined when the system was designed. Any of those can create a legitimate modernization case. But "this system is twenty years old" does not tell me which of those problems actually exists.

Technical debt is usually discussed as the future cost created by design or implementation choices that make software harder to evolve. For modernization decisions, I find it useful to ask a broader question: what continuing cost, constraint, or risk is the existing system imposing on the organization? Age can contribute to those problems, but age is not the problem itself.

A few questions tell me much more than the date of the original deployment:

Question What it actually reveals
Is the platform still supported and patchable? Security and operational risk
How difficult is it to make and test a business change safely? Cost of change
Are integrations constraining new products or channels? Architectural constraint
Is critical knowledge concentrated in a few people? Continuity and staffing risk
How frequently does the system cause incidents or downtime? Reliability risk
What measurable capability would replacement improve? Business case
How old is the application? Context, but not a diagnosis

Industry migration guidance makes a similar distinction, pointing to business constraints, vendor end-of-life, security exposure, integration limitations, and scalability as more useful signals than age alone. That framing is much more useful to an engineering leader.

If a twenty-year-old system is secure, supportable, understood, stable, testable, and inexpensive to change, its age may be the least interesting thing about it. If a five-year-old system cannot be changed safely without breaking three integrations, has no reliable regression testing, and requires manual intervention every night, the calendar should not protect it from scrutiny.

Modernization happens inside a running business

Early in my career, I also worked on a banking automation system. Part of that work involved deploying software at branches. The real constraint was the operating environment: the software had to be deployed at a bank branch that still had customers to serve and transactions to process. The branch could not simply stop operating because the technology team wanted a clean deployment window. Installation and verification had to fit around a business day that was still going to happen.

That experience stayed with me because enterprise modernization is often described as though the organization can step outside itself while the technology is replaced. In reality, claims still have to be processed, policies still have to be serviced, payments still have to move, regulatory reports still have deadlines, and customers still appear at the counter.

The old and new environments therefore often have to coexist, at least temporarily, with operational reality. That may mean staged migration, parallel runs, reconciliation, rollback planning, temporary interfaces, or keeping part of the old environment alive longer than architects would prefer.

That is not automatically a failure of modernization. Sometimes it is the price of changing a system without stopping the business it supports.

Sometimes replacement is absolutely the right decision

There is a danger in making the argument against age-based modernization too strongly. It can turn into nostalgia for old software, which would be just as unhelpful.

Years after the STAR-to-NOVA project I worked on, The Doctors Company announced another major modernization. In 2019, the company selected Duck Creek Suite OnDemand to replace its home-grown core systems. The public rationale included simplifying product delivery, improving customer experience, strengthening analytics, and accelerating service delivery.

That later program was not the same project I worked on, but its stated reasons are instructive. In a later interview, The Doctors Company's COO described a transition to a unified cloud-based digital core and reported that system availability increased from 67 percent to 99.99 percent. She also discussed a stronger security posture.

Those are modernization arguments I understand. Reliability, security, support for new products and channels, excessive cost of change, and genuine business constraints are all defensible reasons to modernize. The age of the application, by itself, is not.

The replacement will start accumulating history too

One of the strange assumptions in modernization discussions is that the old system contains technical debt while the replacement represents a clean state.

A new platform starts accumulating its own constraints almost as soon as it goes live. An integration may require an exception, a release deadline may force a compromise, a new regulatory requirement may not fit neatly into the original architecture, a customer workflow may demand customization, and an acquisition may introduce another source system. Over time, these individual decisions become part of the platform's own history.

A new system will inevitably acquire exceptions and compromises of its own. Some of those decisions become technical debt when they increase the cost or difficulty of future change. Today's modernization program can therefore become tomorrow's modernization problem, and the more realistic objective is to understand and manage that cost as the platform evolves.

Modernization is not a binary choice

The decision is rarely limited to "keep everything" or "rewrite everything." A stable core can sometimes remain in place while troublesome integrations are replaced. A difficult component can be refactored without rebuilding the entire application. Data can move before all application behavior moves. APIs can isolate an older system from newer channels. Some functions can be replaced while historical data remains accessible in an archive. At other times, a full replacement is justified.

A modernization option should be judged by how effectively it removes the constraints that matter while keeping implementation and operational risk within acceptable bounds.

That requires understanding both sides of the equation: what is the existing system costing us, and what new risks, dependencies, and validation obligations will the replacement introduce? A modernization program can fail in either direction: by preserving a system whose constraints have become unacceptable, or by replacing it without accounting for the new risks, dependencies, and validation work the replacement introduces.

Old is not a diagnosis

Working through replacements, migrations, and live-system transitions taught me to evaluate legacy systems by the problems they actually create, not by age alone. If an old platform creates unacceptable security risk, blocks the business, cannot be supported, repeatedly fails, or makes ordinary changes prohibitively expensive, then its replacement may be overdue.

But if the case for modernization begins and ends with the year the system was built, I would keep asking questions.

Software eventually gets old. Technical debt becomes important when the decisions embedded in a system impose a meaningful continuing cost on future change. Operational risk and business constraints can provide additional reasons to modernize, whether or not they fit neatly under the technical-debt label.

Sometimes the right response is replacement. Sometimes it is refactoring, replatforming, isolation, staged migration, or simply leaving a stable component alone.

“How old is this software?” is easy to ask. The more consequential question is: “What is this software costing us, and what will replacing it actually solve?”

Top comments (0)