Use case
Your software still works. Evolving it is the problem.
The application still does what it was built for. But every change takes longer, needs more care, and depends on fewer and fewer people. The question is not whether it is "old": it is what it costs you to move forward.
You may recognise some of this
All of it rarely applies. Two or three of these points are enough to slow a team down for good.
- Every change risks breaking something else, and nobody really knows what until it ships.
- Some technical components no longer receive updates, security fixes included.
- Part of the code is never touched: people work around it rather than go into it.
- Releases are manual, slow, and happen outside working hours.
- Adding a feature that looks simple takes an unreasonable amount of effort.
- Integrations with other tools have become fragile and get repaired case by case.
- Knowledge of the system lives in one or two people.
What happens if nothing is done
Nothing collapses overnight. That is exactly what makes this hard to decide on.
- The cost of change rises quietly
- Each evolution needs a bit more care than the last. The maintenance budget eats into the one for new things, without any decision ever being taken.
- Unmaintained dependencies become a security matter
- A library that no longer receives fixes does not become a problem gradually: it becomes one the day a vulnerability is published.
- The system becomes an argument against the business
- Requests get refused because they are technically expensive, not because they are bad. The software ends up deciding what the company can do.
- One person leaving becomes an operational risk
- When nothing about the system is written down, a resignation or a long absence turns an ordinary change into an investigation.
Rewriting is not always the answer
Four paths are defensible depending on the situation. A full rewrite is the most visible one, and rarely the most sensible.
| Rewriting is not always the answer | When it is the right call | What it involves |
|---|---|---|
| Maintain | The system still meets the need, the changes expected are limited, and the technical risk is under control. | Update dependencies, document what matters, and accept that the application is not a project. That is a decision, not inaction. |
| Modernise progressively | The system is still fundamentally right, but parts of it get in the way: coupling, no tests, manual deployment, a dated interface. | Isolate the risky areas, expose stable interfaces, replace piece by piece. Old and new run side by side for the whole transition. |
| Rebuild | A fundamental constraint genuinely blocks progress: an unsuitable data model, an abandoned platform, an architecture that forbids what the business asks for. | A long project, with data migration and a period of dual running. Only worth starting with a framed scope and a staged path. |
| Replace with an off-the-shelf product | The process it supports is no longer a differentiator, and an existing product covers the need without contortion. | Accepting that some practices will bend to the product, and taking data and accumulated business rules seriously. |
We do not default to the heaviest path. In several situations, maintaining an application properly for two more years is the best economic decision.
Old and new can run side by side
Progressive modernisation is not stopping one system and starting another. At every stage both run, and the share carried by the original application goes down.
- 01
Stabilise
Put tests, automated deployment and monitoring back in place. Nothing changes for users.
- 02
Isolate
Draw a boundary around the risky areas and cut the cross-dependencies that make every change global.
- 03
Expose
Put the data and the rules behind a stable interface that both old and new can call.
- 04
Replace
Rebuild one function at a time, with a controlled and reversible switchover.
- 05
Retire
Remove the code that is no longer needed. While it is still there, it still has to be maintained.
The last stage is the one most often skipped. An old component left in place "just in case" keeps costing in maintenance and in exposed surface.
How we approach it
Before proposing a path, you have to know what the system actually does — which is almost never what the documentation says.
- 01
Understand what exists
Reading the code, mapping dependencies, inventorying what is deployed and tested. We also talk to users: some business rules only live in their habits.
- 02
Identify the risky areas
Where a change propagates, which dependencies are no longer maintained, which parts have neither tests nor documentation, and where knowledge is concentrated.
- 03
Decide what stays
Not every part of a system deserves the same effort. Some are stable and well written: they stay. The conversation is about the rest.
- 04
Secure the interfaces and the data
Before replacing anything, the exchange points get stabilised and the real state of the data gets checked: duplicates, fields repurposed for something else, incomplete history.
- 05
Modernise in increments
One area at a time, released regularly, with a rollback available. The system stays usable for the whole duration.
- 06
Migrate the data
Migration is prepared early, rehearsed, and verified by consistency checks before the switchover. It is the most underestimated part of this kind of project.
- 07
Retire the debt that is no longer needed
Removing replaced components, access that no longer serves anything, and forgotten environments. Without this step, modernisation adds a layer instead of removing one.
On an old system, data migration often deserves its own scoping: that is where the surprises are, not in the code.
What if nobody really knows the code any more?
This is a common situation, and it does not block a project. It just changes how you start.
The running system is the reference
What runs in production is the most reliable specification available. You observe it, trace the calls, and reconstruct the actual behaviour before touching anything.
The data tells you the rules
A field always filled the same way, a value that should not exist, a table nobody writes to any more: the actual content reveals business rules nobody can state out loud.
Tests come before changes
On untested code, the first useful delivery is often writing tests that describe the current behaviour — without fixing it — so it can then be changed without guessing.
What this can look like
A few shapes this kind of path takes in practice, depending on the starting point.
- An automated test and deployment pipeline on an application that used to ship by hand
- A stable API in front of an old system, so other tools stop reaching into its database
- A rebuilt interface on the most-used screens, with the rest kept as it is
- A critical module isolated then rewritten while the rest of the application keeps running
- A data migration with consistency checks and rehearsals before the switchover
- A retirement plan for replaced components, with their access and environments
These describe possible shapes of a solution, not delivered projects presented as references.
This path may draw on
Depending on the starting point, one or more of our areas come in. Scoping decides which.
Frequently asked questions
Do we have to rewrite an old application?
No, and it is rarely the best choice. A full rewrite loses years of accumulated business rules, costs a lot, and produces nothing usable for a long time. In most cases it is better to isolate the part that genuinely causes problems and replace that, while the rest keeps running.
Can we modernise without stopping the system?
Yes — that is the whole principle of progressive modernisation: at every stage, old and new run in parallel. Switchovers happen function by function, with a rollback available. It demands more discipline than a one-shot replacement, but it avoids stopping the business.
How do you take over software with incomplete documentation?
By starting from what runs rather than what is written. Reading the code, observing the system in production, analysing the real data and talking to users reconstructs the actual behaviour. The output of that phase becomes the documentation that was missing.
How is the data migration handled?
As a piece of work in its own right, not as a final step. That means analysing the real state of the data early, defining explicit transformation rules, rehearsing the migration as many times as needed, and planning consistency checks after the switchover. This is where most bad surprises happen.
How long does it take?
It depends entirely on the size of the system and the path chosen, and neither is known before scoping. What we can say: progressive modernisation produces usable results continuously, whereas a rebuild delivers nothing until the end.
Let us talk about your application
Tell us what is blocking today. We will tell you frankly which path looks reasonable — including not starting a project right now.