Almost every established business runs at least one system nobody wants to touch: a Delphi application from the 2000s, a VB6 tool, an early .NET codebase that quietly became business-critical. The question isn't whether to modernize it - eventually, you always do. The question is whether to rewrite it from scratch or modernize it in place, and getting that call wrong is an expensive mistake in both directions.

Why the full rewrite is tempting - and usually wrong

A clean rewrite is seductive: no legacy baggage, modern architecture, whatever stack your team actually wants to work in. In practice, most rewrite projects underestimate one thing badly - the amount of undocumented business logic buried in the old system. Years of edge cases, exceptions and "the customer specifically asked for this in 2014" decisions rarely live anywhere except in the running code. A rewrite has to rediscover all of it, usually the hard way: in production, after go-live, when a workflow silently breaks.

I've seen rewrite projects run 2-3x over budget and timeline for exactly this reason, and a fair number never fully replace the old system - the two end up running in parallel indefinitely, which is the worst outcome on the table.

When a rewrite genuinely makes sense

  • The technology is genuinely dead - no security patches, no available developers, no viable upgrade path (classic VB6 without a runtime story is the textbook case).
  • The business logic itself has fundamentally changed, so the old system's architecture no longer matches how the business actually operates.
  • The codebase is small and well-understood enough that rediscovery risk is low.

Gradual modernization: the strangler fig approach

For most legacy systems, the better path is what's usually called the "strangler fig" pattern: new functionality is built as modern, independent modules that call into or sit alongside the old system, while the legacy core keeps running unchanged. Over time, more of the old system's responsibility moves to the new modules, until what's left of the original codebase is small enough to retire safely - or in some cases, not worth retiring at all.

The practical advantages are significant: the business keeps running throughout, risk is spread across many small, reversible steps instead of one big cutover, and you get to validate that the new architecture actually works before betting the whole system on it.

This is exactly the kind of work I do most often: keeping a Delphi or older .NET core alive and stable while building the new capability - a REST API, a modern web frontend, an AI-powered feature - around it, without a risky big-bang cutover.

A practical decision framework

Three questions tend to settle most cases:

  • Is the current system actually failing the business today, or is it just old? "Old but stable" is not automatically a problem worth a rewrite.
  • How well is the business logic documented - in code comments, in specs, or only in the heads of people who've been there for years? Poor documentation is a strong argument against a full rewrite.
  • Can the business tolerate a multi-month period with two systems running in parallel, or does it need continuous operation with no disruption? Gradual modernization wins almost every time the answer is "no disruption."

Where to start

If you're carrying a legacy system that's become a liability, the first useful step usually isn't a rewrite proposal - it's an honest architecture review: what does the system actually do, where are the real risks, and what's the lowest-risk path to a system your team can maintain confidently. That assessment alone often changes the plan entirely.