Legacy system modernization: integrate instead of replacing
Legacy doesn't mean bad. It means it works, that it has spent years accumulating rules nobody has written down anywhere else, and that replacing the whole thing is a project carrying more risk than the company can absorb in one go.
There is one person who knows how it works. Sometimes they no longer work here and get called when something breaks. The system has no test environment, the documentation is a manual from eight years ago, and every change is tested directly in production at seven in the morning.
Why the full rewrite almost always goes badly
Rebuilding everything is an attractive proposal because it promises to end the problem. In practice it means keeping two systems running in parallel for months, rediscovering business rules that only exist inside the old code, and absorbing the pressure of a long project with nothing visible to show. The alternative that does work is boring: you wrap the old system, you put a translating layer in front of it, and you pull functions out one at a time. Every step delivers something and every step is reversible. The old system gets switched off when it has stopped doing anything, not on a date circled in a plan.
The flows that have to be solved
Each one with the design decision that determines whether it survives production.
A translation layer in front
You build a modern interface that acts as a facade. New systems talk to it and never to the legacy system, so its quirks —numeric codes, fields with two meanings, dates in in-house formats— stay encapsulated in one place.
Without that layer, every new integration learns the legacy quirks and inherits them. By the third one, you cannot replace it without touching everything.
Change capture instead of polling
When the legacy system emits no events, changes are detected in its database —by timestamp, by audit table, or by reading the transaction log— and published outwards as events.
Polling a production database every minute punishes the users and finds out about changes late. And with a timestamp alone you lose deletions, which is exactly what nobody tests.
Replacement in pieces
Functions leave the legacy system one at a time, starting with the ones that have the fewest dependencies and cause the most pain. The facade decides, request by request, whether it goes to the new system or the old one.
Starting with the core module because it is the important one is the fastest way to stall the project halfway. You start at the edges.
When there is no data route at all
Some systems will only let you in through the screen. There, file exchange in an agreed folder still works, or interface automation as a last resort.
Screen automation breaks with any visual change and gives you no transactional guarantees. It is a bridging solution with an expiry date, and it has to be treated as one from day one.
What you get
- ▪ Documentation of the business rules recovered from the old system
- ▪ Translation layer with a stable data contract
- ▪ Change publication out to the rest of the ecosystem
- ▪ Phased replacement plan, with a justified order and points of return
- ▪ A measured reduction in dependence on specific individuals
How we build it
Idempotent by default
Every operation can be repeated without duplicating anything. That is what makes a retry safe, and without it no integration survives its first dropped connection.
Observable state
Every message has a state you can query: pending, sent, confirmed, failed. An integration you only notice when it breaks had already been breaking for a while.
One owner per field
For each field there is one system that decides and the others follow. Bidirectional sync without that rule ends in loops and in data that changes on its own.
AI where it judges, not where it calculates
Classifying, extracting and drafting are model work. Adding up, validating and routing are code work. Swapping them round is expensive and impossible to audit.
How we work
The people who run the diagnosis are the people who build it. No hand-offs. The full method and the rest of the capabilities are on the systems integration page.
- 01 Diagnosis · 3–5 days
We map systems, data flows and the dependencies that actually exist, including the ones nobody wrote down. Output: a closed scope and the list of what is broken today.
- 02 Integration design · 1 week
Data contracts, direction of sync, retry policy and who owns each field. This is settled before any code gets written, because it is the expensive thing to change later.
- 03 Build and testing · 2–6 weeks
Built against the real edge cases, not the happy path. Tested against the actual systems and rolled out in stages.
- 04 Production and observability · ongoing
Status dashboard, alerts when something has gone too long without confirmation, and maintenance of the integrations when third-party APIs change under you.
Related to this case
What usually comes before or after, with the reason we link it.
Digital X-Ray
The unavoidable first step: without knowing what the legacy system does, any replacement plan is a bet.
Open →APIs and webhooks
What gets built in front of the legacy system, once the translation layer exists.
Open →Technology roadmap
To order what leaves the old system first, and on what criteria.
Open →Mapping the systems of a hotel chain
A real case of finding out what was running, what talked to what, and which pieces nobody could explain.
Open →Questions about this case
When is it better to integrate and when to replace? +
Integrate when the system does its job and the problem is that it is isolated, when it holds business rules nobody has documented, or when the operation cannot afford an interruption. Replace when the vendor has disappeared and nobody can maintain it, when the technology blocks something that cannot be solved from outside, or when the cost of keeping it already exceeds the cost of rebuilding it. Most real cases are the first, even though the conversation always opens with the second.
Can a system with no API and no documentation be integrated? +
Almost always yes, though with more work and by less elegant routes: read access to the database, change detection, file exchange or —when nothing else is left— screen automation. What decides viability isn't the age of the system, it is whether some controlled way to read and to write exists. That gets checked during the diagnosis, and it is the first question we ask.
How long does it take? +
The translation layer and the first integration are usually in production in four to eight weeks. Full replacement, if you decide to do it, is measured in quarters and by design has no single cutover date: each function that leaves is a delivery in itself. That is precisely the advantage over a rewrite, not a side effect of it.
What if the person who knew the system has left? +
Then the first phase stops being technical and becomes archaeological: the rules get reconstructed from the data and from observed behaviour, not from the code. It is slower and it is worth saying so up front. It is also why documenting what we find is a project deliverable rather than a courtesy.
Start with the diagnosis
Three to five days to know what gets connected first, with scope and price closed before a line of code is written.
Talk to a specialist ↗Other cases