When a legacy system needs replacing — and when it doesn't
Pedro CunhaPublished on updated on 14 min read
In short
Old, ugly or slow is not a reason to replace: of the nine systems that were already running when we arrived, we replaced two and still maintain five. The signs that actually decide are different ones — nobody can change the system, you lost access to your own product, and the team has already built a path around it.
"It's old and it's ugly" is not a reason to replace#
When someone comes to us to replace a system, the opening sentences are almost always the same: "I built this a long time ago", "I'd like it to look better, it's really ugly", "it's old".
None of the three is a reason. Age is not a defect, a dated interface is fixed for a fraction of what a rewrite costs, and "a long time ago" describes a system that has been working for a long time — which is an argument in its favour, not against it.
The best-known warning about this is from 2000: in "Things You Should Never Do", Joel Spolsky calls a rewrite from scratch the worst strategic mistake a software company can make. The argument is about reading, not about code — it is harder to read code than to write it, so the old system looks worse than it is. What comes across as a mess is usually years of accumulated fixes: every odd line solving a real case nobody remembers any more.
Our own practice confirms it. Over the past few years we took over nine systems that already existed when we arrived. We replaced two.
| System | What it was | What we did |
|---|---|---|
| Reticar | field sales rep app, unmaintained, pulled from the store | replaced |
| DNA Genética | breeding system over ten years old | replaced |
| Management system with around 18 modules | in daily use, the whole operation inside it | replacing now |
| GT | app + web platform + API inherited from another team | rebuilt the app only |
| Cirurgicred | old system, no documentation | maintaining |
| Hybri | live event platform | maintaining |
| UAI Legal | third-party system | maintaining |
| Two legal platforms from the same vendor | untyped JavaScript, no documentation | maintaining |
The most telling case is Cirurgicred. The system is old, has no documentation and did not work correctly — and the client came to us wanting to evolve it. Our conclusion was that evolving it made no sense: small adjustments went in to follow the current shape of the operation, and no new features. Saying that costs revenue in the short term and is the right recommendation.
Sign 1 — nobody can change the system#
This is the sign that shows up disguised as small talk: "the company that built it doesn't exist any more", "the developer doesn't want to work on the project", "nobody really knows how it works".
Notice that none of them is about technology. They are about people and knowledge, and that is what makes them serious: a technically healthy system nobody knows how to change is closer to the end than an ugly system with an active team.
We have seen all three variations:
- At DNA Genética, the system was over ten years old, had been built by a software house and had no clear documentation. What was left was one programmer doing occasional maintenance who did not want to keep developing it.
- At Hybri, the person who built it was a developer who was a partner in the project. He left, and the platform was left with nobody.
- At GT, the app had been written without typing and without documentation — the knowledge was neither in the code nor on paper.
The test is objective and takes a minute: how many people can, today, fix a production bug and ship the fix? If the answer is one, you have a continuity risk. If it is zero, the system has already stopped being an asset and become a liability — it just has not sent the bill yet.
Sign 2 — you lost access to your own product#
This is the most concrete sign of all, and the least written about. It is not about code quality: it is the moment when a third party decides the fate of your system and you have no way to respond.
It shows up in very literal forms:
- The app left the store. That is what happened to Reticar's old app: it stopped keeping up with the required SDK versions and was removed. The client could no longer install their own app — that is not a metaphor for obsolescence, it is an app that no longer exists for the people who need it.
- The app never reached the store. At GT, the inherited app was distributed as a file, straight to the client, and the team still had to walk each person through installing it.
- A dependency is going to be switched off. Hybri's platform ran scripts on a version of Node that was going to be discontinued. The system worked fine — what was about to break was the ground underneath it, on a date that was not the client's.
The same applies to the platform the system was written on. Vue 2 reached end of life on 31 December 2023: according to the project's official page it no longer receives features, updates or fixes — but it remains available on the distribution channels. That is where the risk lives: end of life does not knock anything over, so nothing warns you. TinyMCE 4, a common editor in management systems, has been unsupported since 31 December 2020 — five years without a security fix, still running in production out there.
The question that sums up the section: is there a date, set by another company, on which your system stops working? If there is, the deadline is not yours.
Sign 3 — the team has already built a path around it#
This is the sign the board finds out about last, because it never raises a ticket: the team simply stops using the part that does not help and solves it outside the system.
At DNA Genética, collecting animal traits in the field was done with pen and paper and typed in later — the system existed, but it did not follow the person standing in the pasture. At another client, the internal messaging module sat idle because the team talked on WhatsApp, and a report the system was supposed to produce was assembled by hand in Word, every month.
Nobody decides this in a meeting. It just happens: someone builds a spreadsheet to double-check a number the system shows wrong, someone else starts sending the document over WhatsApp because sending it from inside fails, and within a year the real operation is scattered across five tools nobody purchased.
When that happens, the system has already been partly replaced — by spreadsheet, WhatsApp and Word. The question stops being whether you should replace it and becomes whether you noticed the replacement already started: with no project, no control and nobody accountable for the data that now lives outside.
Sign 4 — the system became the limit of the business#
Slowness on its own is not a sign: a heavy screen is usually a badly written query, and swapping the system because of it is far too expensive. The sign is when performance changes what the company is able to do.
At DNA Genética this was measured with a stopwatch, on the old system, at a real farm. A batch of 32 animals took 30 minutes just to run the breeding match, plus another 19 minutes to print the result — with an error midway that forced a restart. The whole farm, 65 animals across three batches, consumed 1 hour and 35 minutes. On the system we built, tested with twice as many animals, the same operation — run and print — responds in up to 3 minutes.
The number matters less than the consequence: at 1h35 per farm, it was impossible to run the breeding match in front of the farmer. The work had to be taken back to the office and returned later. It was not a slow screen; it was the system deciding how the service could be delivered.
The other form of this sign is quieter: changing one thing starts costing two. In a management system we diagnosed, two practically identical modules had been implemented in parallel, taking up ten tables where four would do — and every new rule had to be written twice, or the two sides drifted apart. In that same system, partner data was stored as fixed configuration keys: adding a new partner was not configuration, it was editing data. In two legal platforms we maintain, written in untyped and undocumented JavaScript, the effect arrived by another route: each requested improvement took so long that the client's product stopped moving.
Summary of the four signs, with what usually sits behind each sentence:
| What the client says | What usually sits behind it | Patch or replace |
|---|---|---|
| "It's ugly, I want to modernise it" | dated interface | patch — a redesign costs a fraction |
| "It's slow" | a badly written query on one screen | patch — unless it changes the process |
| "The company that built it is gone" | nobody accountable for the code | assess — start by taking over maintenance |
| "The app vanished from the store" | nobody kept up with the platform's requirements | replace the affected part, urgently |
| "We control that in a spreadsheet" | the system has already been replaced from outside | replace — or lose the data for good |
| "This part can't be changed" | a business rule became data, or duplicated code | replace when the cost of changing beats the cost of rebuilding |
Can you replace just one part?#
You can — and that is the answer that saves the most money, when it fits. The criterion is whether the part is genuinely separable from the rest.
GT is the example. We inherited from another team a set of systems that came out of a bad experience: the project took four times the estimated schedule and did not deliver the full result. The app had no typing, no documentation and had never been published. Even so, we did not rebuild everything. The app is detachable — it talks to the platform through a stable interface — so we rebuilt only that: typed, documented, with an approved prototype and published to the stores. The web platform and the API stayed, running on Go.
In the two legal platforms we maintain, the gain came from an even smaller cut: we swapped the e-signature tool for another one, lowering cost, without rewriting a single line of the system.
There is an established name for gradual replacement: the Strangler Fig pattern, described by Martin Fowler — build the new around the old and move function after function across until the old one can be switched off. It works, and the condition is always the same: clear boundaries between the parts. Without that boundary, going gradual gets expensive — data that drifts apart between the two systems, two integrations instead of one, and a transition architecture that usually outlives the plan. At that point the fastest route is the direct one: touch the current system as little as possible and build the new one whole.
If you are replacing the whole thing, how the cutover happens#
Replacing a system that is in operation is not living with two systems: it is building the whole thing and cutting over at once.
The path has four stages. The new system is built in full, with partial deliveries going to staging throughout development — the client validates in pieces, but does not operate in pieces. Once ready, it runs in parallel with the old one for a period: the team uses both, compares, and points out what is missing. The cutover happens on an agreed day, with data migrated in a planned window. And the old system stays read-only for a few months as a fallback, before being switched off.
The parallel run is the misunderstood part. It is not for splitting the operation between the two systems — it is for the team to gain confidence before depending on the new one. At Reticar it went like this: during testing the new app ran alongside the old one, with in-person training and a formal round of feedback; only then were the credentials handed over and the old one retired. The migration carried 3,819 customers, preserving identifiers and registration dates — the sales rep opened the app and recognised their own portfolio, instead of an empty system asking them to retype years of relationships.
One scheduling detail is worth more than it looks: the cutover date is chosen against the operation's peak, not against the project timeline. If the operation concentrates between January and May, the right window is between August and October — even if that means waiting.
Frequently asked questions#
Can we replace the system gradually, one module at a time?#
Yes, when the part is genuinely separable — an app that talks to the platform through a stable interface can be rebuilt on its own. When it is not isolated, splitting the operation across two systems gets expensive: data that drifts apart, two integrations instead of one, and a temporary architecture that outlives the plan. At that point it is faster to build the whole thing and cut over at once.
How long does it take to replace a system the company uses every day?#
For a full management system, with many modules and years of accumulated data, the realistic span between the start of development and the cutover is ten to twelve months — and much of that time is not writing code, it is uncovering the rules nobody documented. For a standalone app or module, it is a fraction of that. In both cases the old system keeps running until cutover day.
Do I need the old system's source code to replace it?#
It is not mandatory, but it changes the cost and the risk. Without it, business rules have to be rediscovered by reverse engineering — watching the system in use and interviewing the people who operate it — which is slow and leaves room for divergence. With the code, the implicit becomes verifiable. If the previous vendor still exists, negotiate access before you start.
What happens to the data and the history from previous years?#
It migrates, and that is the most underestimated part of the project. The thing to watch is preserving original identifiers and dates, so that whoever opens the new system recognises their own records instead of an empty screen. The migration has to be rehearsed more than once before the cutover, and the old system usually stays available read-only for a few months afterwards.
My vendor disappeared or stopped answering. Is that reason enough to replace?#
It is reason enough to act, and replacing is the most expensive option available. An absent vendor is a contract problem, not a code problem: in most cases the way out is handing maintenance to another team, which takes the system as it stands. That also works as a cheap diagnosis — after a few months living with the code, replacing or not stops being guesswork.
Sources#
- Joel Spolsky — "Things You Should Never Do, Part I" (2000), on the risk of rewriting from scratch — https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/
- Martin Fowler — "Strangler Fig Application" (the gradual replacement pattern) — https://martinfowler.com/bliki/StranglerFigApplication.html
- Vue.js — "Vue 2 has reached End of Life" (the project's official end-of-life page) — https://v2.vuejs.org/eol/
- Tiny — "TinyMCE 4 support window" (official announcement of the end of TinyMCE 4 support) — https://www.tiny.cloud/blog/tinymce-4-support-window/
Next step#
If you recognised two or more of these signs, the next step is not to request a rewrite proposal — it is to run a diagnosis. In most of the cases we have handled, the right outcome was taking over maintenance and fixing what was blocking the operation. When replacing is the answer, it usually applies to the whole: that was the case at Reticar Motores, where we replaced the field sales system of a live operation. It is the kind of decision we work through in custom systems — and, if your team works where the signal drops, it is worth also reading what changes in a system when the app has to work with no signal.