How to Modernize an Old Website or Legacy Business System | POLPROG Skip to content

Learning

How to Modernize an Old Website or Legacy Business System

Published: 10 min read POLPROG Modernisation
Circuit board representing the transition from legacy to modern technology

Legacy systems get replaced badly more often than they get replaced. The difference is rarely the technology, it is whether the team modernised in small, continuous steps or tried to do everything at once.

Old systems have two things in common: they work, and they make everyone nervous. They work because years of real-world use shaped them around the actual business. They make people nervous because nobody wants to touch them, documentation is thin, and one of the original developers left three jobs ago.

What actually counts as "legacy"

Not every old system is legacy. A 10-year-old application running on a supported stack, well-tested, with active maintenance, is simply mature. Legacy typically means:

  • Platform or framework versions that no longer receive security updates.
  • Knowledge concentrated in one or two people.
  • Fear of changing things because small changes break distant features.
  • Manual deployments, manual tests, manual everything.
  • A widening gap between what the business needs and what the system can do.

The two failing approaches

  • Do nothing. Risk compounds. Eventually something critical breaks at a bad moment.
  • Rewrite from scratch. A big-bang rewrite almost always takes longer than planned, discovers hidden requirements too late, and runs in parallel with the old system for so long that the team gives up.

The approaches that actually work sit between these two extremes.

Strategy 1: Strangle the old system

Keep the legacy system running, but gradually route new functionality through a new layer. Over time, more and more features live in the new system; the old one gets smaller until it is finally turned off.

This works for websites (move page by page), internal tools (move module by module) and integrations (introduce a new API gateway, then redirect callers to it).

Strategy 2: Modernise in place

Sometimes the architecture is fine, but the stack is too old. In that case, upgrade one layer at a time: language/runtime, then libraries, then framework, then deployment. Add automated tests as you go, so each step is verifiable. Not glamorous, but very effective.

Strategy 3: Split, then modernise

Large monolithic systems are often easier to modernise piece by piece once the seams are clear. That means first defining boundaries inside the existing system (modules, bounded contexts), then replacing modules independently. The boundaries matter more than the technology choice.

What to do before writing any code

  • Write down what the system actually does today, not what it was supposed to do.
  • Identify the features people rely on daily, those must keep working through the transition.
  • Identify dead features, they're often a meaningful share of the code and should be quietly retired.
  • Take inventory of data, integrations, permissions and exports. This is where modernisation projects usually run into trouble.
  • Decide what "done" means in measurable terms.

Risk controls that make a real difference

  • Backups and tested restores, before any change.
  • Automated tests for the behaviour you care about, introduced gradually.
  • Feature flags so changes can be rolled back in production without a deploy.
  • Observability, logs, error tracking, key business metrics, so you find out quickly if something regressed.
  • A staged rollout: new code runs for internal users first, then a small percentage of real users, then everyone.

Strangler fig pattern. Instead of a risky big-bang rewrite, you route traffic through a thin facade that gradually forwards more and more requests to new services, while the legacy system keeps handling the rest until it is empty.

Example: a minimal facade route

A small gateway can decide per-route whether a request goes to the legacy monolith or the new service. Written in a framework-neutral way:

app.use((req, res, next) => {
  if (req.path.startsWith('/api/v2/invoices')) {
    return proxy(req, res, NEW_SERVICE_URL);
  }
  return proxy(req, res, LEGACY_URL);
});

Each migrated module moves one prefix from LEGACY_URL to NEW_SERVICE_URL. Users never see a big-bang cutover; the team ships small, reversible steps.

Common trap: writing the new system and the old one as if they must be feature-equivalent on day one. They don't. Start with the smallest piece that has real business value, prove the migration path, then repeat.

Common mistakes

  • Making modernisation about the technology choice. Fancy new stacks don't help if the data model and integrations are still mysterious.
  • Waiting for a complete redesign before fixing the visible problems. Users pay the cost; momentum dies.
  • Turning off the old system too early. Run both in parallel long enough to trust the new one.
  • Losing institutional knowledge. Pair with the people who know the system. Their knowledge is the real asset.

A realistic timeline

  1. Stabilise: backups, monitoring, security patches, minimum tests.
  2. Map: document what exists and what is actually used.
  3. Prioritise: pick the piece that is painful now and valuable to modernise first.
  4. Replace: build the new version of that piece alongside the old.
  5. Switch: route traffic, monitor, fix issues.
  6. Retire: decommission the old piece only after it has been quiet for a while.
  7. Repeat for the next piece.

Modernisation succeeds when it is incremental, observable and tied to specific business value. Stabilise first, map what exists, replace one meaningful piece at a time, and keep the business running throughout. Boring beats heroic, and it's the only approach that reliably works.

Legacy Modernisation Refactoring

Frequently asked questions

Is it ever worth rewriting from scratch?

Occasionally, for small systems or when the underlying platform is truly unrecoverable. For anything of real size and age, incremental modernisation, keeping the business running while replacing parts, is almost always safer and cheaper.

How do we decide what to modernise first?

Pick the intersection of painful today and valuable to change. A module that breaks often, blocks a business goal, and is well-understood by someone on the team is a perfect candidate. Avoid starting with the most complex, least understood area.

What about data migration?

Data is usually the hardest part. Plan for it explicitly: export format, mapping rules, validation, a dry run against real data, and a rollback plan. Many modernisation projects stall because data edge cases were discovered too late.

Can we do this without a dedicated team?

For small systems, sometimes. For anything business-critical, no, at least not quickly. Modernisation needs sustained focus, not spare hours. That's why incremental approaches work: they let a small team deliver value without needing a full parallel organisation.

Was this helpful?

Get new articles by email

One short email per new Learning article. No spam, unsubscribe in one click.

We only use your email to send new articles. No third-party sharing.

Back to Learning