Introduction
Every enterprise has at least one: the system that runs a core part of the business and that nobody wants to touch. It works, mostly. It is also a decade or more old, written by people who have since left, documented in a wiki that stopped being true years ago, and wired into everything. Modernizing it is on the roadmap every year and gets deferred every year, because the obvious way to do it — rewrite it — is the way most modernization projects fail.
This article is about the alternatives: how to modernize a critical system without betting the business on a multi-year rewrite.
Why the big-bang rewrite is the default — and why it fails
The instinct is understandable. The code is old and unpleasant, so the tempting move is to throw it away and build a clean replacement. Engineering leaders have warned against exactly this for decades; rewriting a working system from scratch is one of the most reliable ways to burn years and still end up behind.
Three things go wrong. First, a rewrite asks the business to stand still — or to maintain two systems — while you rebuild the thing the business depends on. Second, the old system encodes years of hard-won behavior, and a clean rewrite quietly drops the parts no one remembered to specify. Third, and most fundamentally, rewrites start from assumptions about what the system does, when the whole problem is that nobody actually knows anymore.
Modernization does not fail because the new code is hard to write. It fails because it starts from a guess.
The real problem isn't the code — it's the knowledge that left with it
The hardest asset to recover in a legacy system is not the source; it is the intent behind it. Buried in that code are the exceptions, the special cases, and the rules that only exist because a regulator or a large customer once demanded them. None of that is written down where you can read it. It lives in the code's behavior and, until recently, in the heads of people who are no longer there.
That is why the first job of any serious modernization is not writing new code. It is making the existing behavior explicit — turning a black box back into a specification you can read, question, and trust. Skip this step and you are not modernizing; you are re-guessing.
The modernization options, and when each fits
There is no single "modernize" button. The well-known framework — often called the 6 Rs — lays out the realistic choices, and mature programs apply different ones to different components rather than one to everything:
Rehost ("lift and shift"). Move the system as-is to new infrastructure. Fastest and lowest-risk, but it carries the debt with it — you have new hosting, not new software.
Replatform. Move it with minimal changes that take advantage of the new platform (managed databases, containers) without rearchitecting. A pragmatic middle step.
Refactor / rearchitect. Restructure the code and architecture while preserving behavior. Higher effort, real long-term payoff.
Rebuild. Rewrite the component from a recovered specification. Reserve this for the pieces where it genuinely pays off — not the whole estate at once.
Replace. Retire custom code in favor of a commercial product where the capability is not a differentiator.
Retire / retain. Some of it should simply be switched off; some should be left alone for now. Deciding what not to touch is part of the strategy.
Layered on top of these is the strangler fig pattern: instead of replacing the system in one cutover, you build new capabilities around the edges and gradually route traffic to them until the old system is doing less and less, and can finally be switched off. The old and new run side by side; risk is spread across many small, reversible steps instead of one irreversible launch.
Recover the business logic before you rebuild it
Whatever mix of Rs you choose, the safe path runs through the same gate: reconstruct what the system actually does before you change it.
In practice that means reading the existing codebase and extracting the requirements it truly implements — the rules, the edge cases, the data flows — into a specification a human can review. Each recovered rule should link back to the code it came from and forward to whatever replaces it, so that "why does this exist?" always has an answer, and so the new system can be shown to be faithful to the old one rather than hopefully similar.
Characterization tests — tests written against the current behavior before you touch it — turn that specification into a safety net. If the new implementation passes the same tests the old system's behavior defined, you have evidence, not optimism.
This reconstruction work used to be the most expensive and least reliable part of modernization, done by hand by whoever could be spared. It is also exactly the kind of work AI is now genuinely good at: reading a large, undocumented codebase and proposing the requirements it implements, for a human to confirm.
How to modernize without freezing the business
The through-line of every successful approach is the same: never stop the business to do it.
Work in parallel. Structure the work so many components can be modernized at once, each with its own tests and dependencies, rather than as one serial mega-project.
Cut over incrementally. Route one capability at a time to the new implementation behind a facade, so a problem affects one slice, not the whole system.
Keep the old system running until the new one has proven itself in production for that slice.
Make it traceable. Maintain the chain from the old rule to the new commit, so an audit — or a bug — can be followed end to end.
Speed comes from parallelism and automation; safety comes from incremental cutover and evidence. You do not have to choose between them.
A practical sequence
- 01Map and prioritize
Inventory the estate; rank components by business risk and value.
- 02Recover the logic
of the highest-priority components into a reviewed specification, with characterization tests.
- 03Choose an R per component
— not one strategy for everything.
- 04Rebuild incrementally
behind a facade, in parallel where possible.
- 05Cut over
one capability at a time, gated by the tests that define correct behavior.
- 06Decommission
the old paths as they are starved, and keep the trail.
Some enterprises run this internally; others bring in a partner to do it as a done-for-you outcome. (Reading a legacy codebase to reconstruct its requirements, then rebuilding it into an owned, standard stack, is one of the two core engagements Tmob AI Studio is built around.) Either way, the principle holds: read the legacy, make it explicit, and replace it a piece at a time — without ever freezing the business to do it.
