The most dangerous sentence in software is "let’s just rewrite it." Rewrites start with optimism, spend a year rebuilding features the business already had, and end with two systems in production: the old one, still running everything, and the new one, still not finished.
We have modernized systems in banking, mining, and logistics, some older than the engineers maintaining them. The playbook below is the one we follow every time. It is slower to describe than a rewrite and much faster to finish.
Step 1: map what actually runs
Legacy systems accumulate mythology. Half the features everyone fears touching have not been used in years, and the report nobody documented is the one finance depends on every month-end. Before any code moves, spend two to four weeks measuring reality.
- Add access logging to every endpoint, screen, and report. Run it for a full business cycle, including month-end.
- Interview the operators, not just the managers. The person who runs the nightly batch knows where the bodies are buried.
- Map every integration in and out: file drops, direct database reads by other systems, scheduled emails. Direct database reads are the ones that hurt later.
- Rank each capability by usage and business risk. This ranking becomes your migration order.
On a mining operations platform for TerraCore Resources, this audit found that 60% of the legacy screens had zero use in 90 days. The modernization scope shrank by more than half before a line of new code existed.
Step 2: put a front door in place
The strangler fig approach only works if every request passes through a point you control. Put a reverse proxy or API gateway in front of the legacy system first, routing 100% of traffic straight through. This step changes nothing for users and gives you the lever that makes everything else safe: the ability to route any single capability to a new implementation, and to route it back in seconds if it misbehaves.
Step 3: migrate in slices, hardest data first
Pick the first slice for learning value, not size: something used daily, with messy data, but low blast radius if it stumbles. Build the new implementation, run it in shadow mode where it processes real traffic and its outputs are compared against the legacy system, and only cut over when the diff report is quiet for a week.
Data migration deserves more respect than it gets. Legacy data is dirty in ways no one remembers deciding. Write migration scripts that are idempotent and re-runnable, validate row counts and business invariants after every run, and keep a reconciliation report the business can read. Where the old and new systems must coexist for months, sync one direction only. Two-way sync is where migrations go to die.
Step 4: retire loudly
A slice is not done when the new code ships. It is done when the old code is off. Turn off the legacy path, watch for screams for two weeks, then delete the code and the infrastructure. Skipping this step is how companies end up paying to run both systems for years. Celebrate each retirement. It keeps the team and the budget owners aware that the project is finishing, not just growing.
What the numbers look like
60%
of legacy screens unused in 90 days on a typical audit
6-8 weeks
typical duration of one migration slice
0
planned downtime across our last four modernizations
A mid-size modernization run this way typically lands in 9 to 18 months, delivering value from the first slice onward. The failed rewrites we have been called in to rescue had all burned longer than that before shipping anything.
We wrote about the security work that rides along with every modernization in security by design. If you are staring at a legacy system and dreading the conversation, book a consultation and we will help you size the real scope.
