If the Software Works, Why Fix It?

The system is live. Customers use it every day. Orders go through, invoices go out, the reports arrive on time. Nothing is on fire.

So when somebody proposes spending money to improve it, the objection is immediate and entirely reasonable: why interfere with something that works?

That instinct deserves respect. Changing working software costs money and carries risk, and anyone proposing it should be made to justify it. "The code is old" is not a justification. Neither is "the developers don't like it".

But "it works" answers only one question: is the software doing its current job? It says nothing about what the software costs to keep doing that job, or what it will cost when the job needs to change. The first two articles in this series looked at why apparently simple changes become slow and expensive. This one asks whether that is actually a reason to act, or whether it is something a business can reasonably choose to live with.

The honest answer is that it depends. The rest of this article is about what it depends on.

Working and healthy are not the same thing

A system can be operationally successful and economically unhealthy at the same time.

Operationally, it continues to serve customers, process transactions and generate revenue. It is available and stable. By every measure that shows up on a dashboard, it is fine.

Economically, the picture can be quite different. Each change takes more effort than it should. Estimates are unreliable because nobody can say with confidence what a change will touch. Certain areas are quietly avoided. A small number of people hold most of the knowledge about how it really works, and the organisation has started to plan around their availability.

None of these things stop the software working. They change what it costs to own.

This distinction matters because the people who see the operational picture and the people who feel the economic one are often different. A CEO or CFO sees a system that runs the business. The engineering team sees a system that resists being changed. Both are right, and a decision made on only one of those views is a decision made on half the information.

Doing nothing is also a decision

"Leave it alone" sounds like the absence of a decision. It is not. It is a choice to keep paying whatever the system currently costs, in whatever form that cost takes.

For long-lived software, maintenance and evolution can represent a substantial part of total lifecycle cost; the Software Engineering Institute at Carnegie Mellon has pointed to research suggesting it is frequently the larger part. The precise proportion varies enormously, and no single figure applies to every system. The useful point is simpler: an existing system is an ongoing investment, not a sunk cost, and its future costs and benefits can be weighed like any other.

Some of that ongoing cost is unavoidable and perfectly healthy. Software that supports a changing business has to change too, and that work needs to be paid for. The question is not whether maintenance costs money. It is whether the system is making that maintenance disproportionately expensive.

The earlier articles in this series covered the mechanics in detail. As a reminder, the recurring charges tend to look like this:

  • investigation that should take an hour takes a day;

  • testing has to be broad because nobody is sure what a change will affect;

  • problems that were fixed come back in a slightly different form.

Each is paid in engineering time and appears on no invoice. Whether that charge is commercially significant is the real question. For some systems it is a rounding error. For others it quietly shapes the entire development budget.

The hidden cost is the work that does not happen

Engineering capacity is finite. That obvious fact is the root of the strongest commercial argument for taking difficult software seriously.

When a team spends an extra two days understanding how a change will ripple through a tangled area of the system, those two days are not lost in the abstract. They are taken from something specific: a customer feature, a pricing change the sales team has been asking for, an integration that would open a new channel, a piece of operational tooling that would save the support team an hour a day.

The business rarely sees this trade directly. What it sees is a roadmap that moves more slowly than expected, features that keep slipping a quarter, and a team that seems busy without the output to match. The connection between the difficulty of the codebase and the thinness of the delivered roadmap is real, but it is indirect, and so it tends to go unexamined.

The research supports the intuition. A large-scale study at Google across more than a thousand projects found that higher architectural complexity was associated with more code being written to fix bugs rather than to deliver features. Other work on how developers spend their time, and on the link between code quality and productivity, points the same way. None of these findings are benchmarks you can apply to your own organisation, and they should not be quoted as if they were. What they establish is that the technical environment affects where engineering effort goes, and therefore what the business gets for it.

This is why "it works" is not the end of the conversation. The cost of a difficult system is not only what it takes to keep it running. It is the work the organisation is not doing because that is where the capacity went.

Risk does not require anything to be broken today

There is a second reason "it works" can be misleading, and it concerns risk rather than cost.

It helps to separate two kinds of risk. Failure risk asks: what happens if the system stops working? Change risk asks: what happens when the business needs it to work differently?

A system can score well on the first and badly on the second. It may run for years without a serious outage while being almost impossible to change safely. That is a stable system with a serious problem, and the problem only becomes visible when the business needs something from it: a regulatory change, a new product line, a shift in how customers want to pay, an acquisition that needs integrating.

Risk also comes from specific conditions rather than from age. The UK Government's Legacy IT Risk Assessment Framework is useful here precisely because it does not treat "old" as a risk category. It looks at concrete factors: whether components are still supported, whether the skills to maintain the system still exist, whether known security vulnerabilities can actually be addressed, whether the system can meet current and likely future needs, and whether it creates dependencies that make other projects slower or more expensive.

Each of those is a real exposure that can exist in a system that is working perfectly today. Software that cannot receive security updates is exposed whether or not anything has gone wrong yet. A system whose behaviour is fully understood by two people is an organisational dependency whether or not either of them is planning to leave. Neither condition means failure is imminent. Both mean the organisation is carrying a risk it may not have priced.

It is worth being equally clear about the opposite error. An old system with supported components, adequate documentation and knowledge spread across a team is not high-risk simply because it was written a long time ago. Age is a prompt to look. It is not a finding.

Difficult is not the same as worth fixing

Everything so far could be read as an argument for improving difficult software. It is not. It is an argument for looking properly before deciding.

Plenty of software should be left alone, and the reasons for leaving it alone are just as commercial as the reasons for changing it. A component that is stable, rarely changes, meets the business's current needs and is reasonably well understood may deserve no investment at all, even if an engineer would wince at the code. Untidy is not the same as expensive. A messy module that nobody has needed to touch in three years is costing very little.

The same goes for software that is already on a path to replacement or retirement for other reasons, or where the honest estimate of fixing a problem exceeds the honest estimate of living with it. Government guidance on managing legacy technology lists "retain" as a legitimate option alongside migration and replacement, and Microsoft's own cloud adoption guidance does the same for stable workloads with little expected return from change. These are not grudging concessions. They are recognition that doing nothing is sometimes the right call.

What should not drive the decision is any of the following: the technology being old, newer frameworks existing, the architecture looking untidy, or somebody having attached the label "technical debt" to it. Technical debt is only commercially meaningful when it creates cost, risk, delay or constraint that the business actually feels. A great deal of what gets called technical debt does none of those things, and fixing it would be an expensive exercise in tidiness.

The distinction that matters is between technical condition and commercial relevance. A moderately awkward area of the system that sits underneath frequent, commercially important change probably deserves attention. A badly written area that nobody needs to change probably does not. The code quality of each tells you very little on its own.

When does intervention become worthwhile?

Bringing this together, the decision principle is reasonably simple to state, even if it is not simple to apply.

Intervention is justified not because software is old, difficult or untidy, but when the expected cost, risk or constraint of leaving a problem alone becomes greater than the cost and risk of dealing with it.

That is not a formula, and it should not be turned into one. It is a way of making sure the right things are weighed. In practice it comes down to a handful of questions asked about a specific area rather than about the system as a whole:

  • What is leaving this area alone actually costing or exposing us to, and how often does that bite?

  • Is the situation stable, or is it getting worse?

  • How much change does the business expect to need here over the next couple of years?

Alongside those, the questions on the other side of the ledger: what would changing it cost, what new risk would the change introduce, and what would the intervention make possible that is not possible now?

The final question is the one most often skipped: how confident are we in any of these answers? Decisions about software are frequently made on recollection and anecdote because that is what is available. An area is "a nightmare" because the one developer who works on it says so. Another area is "fine" because it has not caused a visible incident. Neither is evidence. Before committing significant money either way, it is worth establishing what is actually true about where effort goes, where the dependencies really are and which areas the business genuinely needs to change. That is the point at which an evidence-led assessment of the codebase earns its place: not as a prelude to a project, but as the thing that tells you whether a project is warranted at all.

Action does not mean rewrite

One more distinction, and then the ground is prepared for the next article.

Deciding that an area deserves attention is not the same as deciding what that attention should look like. The possible responses fall into four broad groups:

  • Protect what exists: documenting an area properly, adding tests around fragile behaviour, or putting clearer boundaries around a component so its problems stop leaking into everything else.

  • Improve what matters: repairing something specific, or refactoring the parts that repeatedly cause cost or delay.

  • Change the approach: rebuilding, replacing, buying or retiring, where the evidence supports it.

  • Leave it alone: having understood the area properly and decided that further intervention is not justified.

A problem can justify action without justifying the largest possible action. Government service design guidance makes this point in its own way, describing approaches that wrap or isolate existing systems and improve outcomes without replacing the whole thing. The right response is the proportionate one, and proportion is impossible to judge without knowing what the problem actually is.

So, why fix it?

Working software is a reason to be careful. It is not evidence that the status quo is free.

The fact that a system still does its job tells you that it is operating today. It tells you much less about the risk it is carrying, what it costs to change, what work is not being done because of it, or how it will cope when the business needs something different from it. Those are the questions that decide whether leaving it alone is the cheap, safe option it appears to be.

Sometimes it is. Some software should be left exactly as it is, and the organisation that recognises this saves itself a great deal of money and disruption. Sometimes it is not, and the cost of doing nothing has been accumulating quietly in slower delivery, deferred features and concentrated knowledge for years.

The difference between the two is not visible from the outside, and it is not settled by the age of the code. It is settled by evidence about cost, risk, constraint and what the business will need next.

Once that evidence shows an area is worth acting on, the next question is what to do about it, and the answer is very rarely "rewrite everything". That is the subject of the final article in this series: How Do You Improve Difficult Software Without Rewriting Everything?

Book an introductory call.

Next
Next

Why Small Software Changes Blow Up