The argument for a rewrite is usually an argument about discomfort
The case for replacing a system tends to arrive in the language of engineering — the framework is unsupported, the tests are absent, the architecture has drifted — but the feeling underneath it is usually simpler than that. The system is unpleasant to work in, every change takes longer than it should, and nobody is confident about what a release will break.
Those are real problems and they are worth money to fix. What does not follow is that replacing the system is the cheapest way to fix them. Discomfort is evidence that the code is hard to change; it is not evidence that the requirements encoded in it are wrong.
The hidden requirement problem
A system that has been in production for years contains decisions nobody wrote down. A conditional handling one customer's unusual tax situation. A retry that exists because a supplier's API times out on Monday mornings. A validation rule relaxed after it rejected something legitimate. None of these appear in a specification, because there is no specification — the behaviour is the specification.
A rewrite starts from what people can articulate, which is a subset of what the system does. The gap between those two is discovered in production, by users, over the following year. That is the part of the cost the proposal never contains, because by definition it lists only the requirements someone remembered.
The corollary is uncomfortable but useful: the older and more embedded the system, the more knowledge it holds that nobody can currently state, and therefore the more a rewrite will cost relative to the estimate.
This is also why the second system so often disappoints. The team building it has the original's visible structure to react against and none of its accumulated corrections, so the new architecture is cleaner in exactly the ways the old one was criticised for and unproven in every way the old one had already been tested by reality.
The parallel-running problem
A replacement that takes a year has to be delivered into a business that does not stop for a year. New regulations arrive, a supplier changes their process, someone sells to a customer with an unusual requirement. Each of those has to be built twice — once in the system currently running the business, once in the system intended to replace it.
That duplicate work is rarely in the plan, and it compounds: the longer the rewrite runs, the more of it accumulates, and the more tempting it becomes to freeze changes to the old system in order to let the new one catch up. Freezing the system the business actually runs on, in order to finish software that does not work yet, is a difficult position to be in and a common one.
Phased change avoids this because there is only ever one system. A change made during the work is made once, in the place it belongs, and it is not lost when the migration lands.
What phased change asks for instead
The alternative is not to leave the system alone. It is to make change safe before making change large. That usually starts by characterising current behaviour in tests — not the behaviour the documentation describes, but the behaviour the software actually has, including the parts that look like bugs until you find the customer who depends on them.
With that net in place, the system can be restructured in pieces while it stays in service: a module extracted behind an interface, a dependency upgraded, a path rerouted through new code with the old path still available. Each step is small enough to reason about and reversible enough to ship on a Tuesday.
This is slower to describe and faster to deliver value, because the business keeps running on software that keeps working while it improves.
When a rewrite genuinely is the answer
Sometimes it is. When the platform has no supported upgrade path, when the data model cannot express something the business now needs and cannot be migrated to, when the licensing or hosting position is untenable, or when the system is small enough that its behaviour genuinely can be enumerated — replacement is the honest recommendation.
The distinction worth holding is between a rewrite chosen because the constraints demand it and a rewrite chosen because the existing code is unpleasant. The first is an engineering decision. The second is an expensive way to relocate the same problems into a codebase nobody understands yet either.
A question worth asking first
Before scoping either path, it is worth establishing what specifically is expensive today: is it the cost of each change, the risk of each release, the difficulty of hiring for the stack, or the inability to do something the business now requires? Those four problems have different cheapest solutions, and only one of them frequently resolves to replacement.
That assessment is worth doing independently of whoever would deliver the resulting work, which is why we treat it as its own engagement rather than as the opening phase of a build.