Pillar page

Legacy System Modernization

Incremental monolith-to-microservices refactoring, migration from outdated technologies to modern stacks, cloud migration — without business downtime, with a rollback plan and complete audit trail.

Every company has some legacy system. Some have several. An accounting application from 2008 that somehow still works. A CRM written by consultants nobody remembers anymore. A warehouse running in Access. Each of them will need to be replaced eventually — the question is not whether, but when and how.

Legacy system modernization is one of the hardest types of IT project. It requires balancing three forces: business continuity (the system must keep running the entire time), introducing modern technologies (microservices, cloud, AI), and risk control (every refactor can break something that has worked for years).

Why not just „rewrite from scratch"?

From our experience, 9 out of 10 successful modernizations are incremental refactoring, not rewrites. Rewrites are conceptually tempting („we'll start with a clean slate"), but in practice they have three fundamental problems:

  • Invisible business logic. The old system contains years of business rules — special conditions for the largest customers, tax exemptions for specific industries, workarounds for 2015 regulations. Most of it is undocumented. A rewrite has to recreate all of it from people's memories or code analysis.
  • Duplicated work. While the new system is being written, the business still demands changes in the old one (regulations, new customers, small bugs). The team either duplicates the work (changes in two places) or freezes the old system (business risk).
  • Big bang deployment. After a year of work, the new system is „almost ready". Switching all users in one night generates monumental risk. Any unexpected problem means going back to the old system, losing team morale and eroding business trust.

Incremental refactoring (usually following the Strangler Fig pattern) solves all three: business logic is discovered gradually, one source of truth for each entity, deployment in stages with feature flags.

Six modernization patterns

Each one addresses a specific risk. In most projects we combine several, choosing a pattern per module.

Strangler Fig

Gradually „wrapping" the old system with new components. Old code still runs, but every new feature goes to a new microservice and existing modules are replaced one by one. After 12-24 months the old monolith is switched off.

Anti-corruption layer

An adapter protecting new code from the quirks of the old system (unreadable field names, weird date formats, inconsistent types). All the „ugly" logic is isolated in one place — new code operates on a clean domain model.

Database refactoring

Patterns from Refactoring Databases (Ambler/Sadalage): expand-and-contract for schema migrations, data validation before dropping old columns, running both schemas in parallel during application migration.

Branch by abstraction

Introducing an abstraction layer around the old component, implementing the new component in parallel, gradually switching traffic from 0% to 100% (feature flag). Without a „big bang" deploy.

Shadow mode

The new code runs alongside the old one — both process the same requests, but only the results from the old system reach the user. Results are compared offline. After confirming compatibility (typically 2-4 weeks) we switch traffic to the new code.

Event sourcing for migration

We record the stream of business events from the old system and replay it in the new one. This allows preliminary validation of the new architecture without production risk, and the ability to return to any historical state.

Typical modernization roadmap

For a mid-sized system (a monolith of ~200k lines of code, 5-10 business modules):

  1. Month 1: Discovery and documentation. Reverse engineering the architecture, mapping dependencies, identifying data flows, documenting business processes with help from business stakeholders.
  2. Month 2: Target architecture and pilot. Designing the new architecture, technology selection, pilot on the simplest module (proof of concept). First validation of the approach.
  3. Months 3-4: Carving out the first production module. Strangler Fig pattern, shadow mode for 2-3 weeks, traffic cutover, hypercare. First real business value.
  4. Months 5-12: Iteratively carving out subsequent modules. Each in a 4-6 week cycle: refactor → tests → shadow → production → hypercare. Continuous improvement of the process, reducing time per module.
  5. Months 12-18: Data migration and monolith decommissioning. Once all critical modules are carved out, we finalize the historical data migration, switch off the old system, archive. Celebrate.

Legacy system vs. modernized

AspectLegacy system (typical)After modernization
Time to ship a new feature4-8 weeks (high regression risk)3-7 days (automated tests minimize risk)
Test coverage5-15% (or none)>80%, in the CI/CD pipeline
Developer availabilityLow (outdated technology)High (popular, modern stacks)
SecurityOld libraries with unpatched CVEsOWASP scanning, gitleaks, automated updates
ScalingVertical (more resources for the monolith)Horizontal (scaling specific microservices)
ObservabilityLogs in files, no metricsPrometheus + Grafana + Sentry + SIEM
Compliance (GDPR, EU AI Act, ISO 27001)Demanding, expensive to proveBuilt into the architecture, audit-ready

Six typical risks — and how we address them

Risk: No tests in the legacy system

Mitigation: First we build characterization tests (capture tests) — recording the current system behavior based on production logs and traffic captures. Only then do we start refactoring, with tests as a safety net.

Risk: Knowledge concentrated in one person („truck factor 1")

Mitigation: Knowledge transfer starts in the first week of the project. All meetings with the person who knows the system are recorded and transcribed, key processes documented, architectural decisions justified. After the project the whole team understands the system.

Risk: Temporary team slowdown

Mitigation: For the first 2-3 months the team maintains the old system + builds the new one. A natural slowdown in change velocity. We mitigate it by: prioritizing changes that go only to the new system, freezing low-priority changes in the old codebase.

Risk: Data migration

Mitigation: Every data migration has three phases: dry-run (on a production copy), staging (in test environment with real-scale data), production (in a service window or incrementally). Rollback plan ready before start.

Risk: Organizational resistance

Mitigation: Communication with the business from day one: why we modernize, what changes for the user, what's the schedule, how we measure success. The first iteration is chosen to quickly show tangible value (e.g. a new UI or a faster report).

Risk: Cost underestimation

Mitigation: Discovery (1-2 weeks) before project pricing. 2-3 week iterations with concrete deliverables — easier to correct course than in a long „all at once" project. Budget with a 20-30% buffer for the unexpected.

Frequently asked questions

What is a legacy system?
A legacy system is software that still runs in the organization but is based on outdated technology, carries deep technical debt, lacks tests, lacks documentation, or lacks the developer who wrote it. Classic examples: a monolithic PHP 5.x or .NET Framework 4.0 application, a database with no migrations, a jQuery frontend, FTP deployment. It works, but every change is high-risk and high-cost.
Why modernize if the system works?
Three main reasons. First: maintenance cost grows exponentially with system age — fewer developers know the technology, every change takes longer, every bug has a wider blast radius. Second: security risk — old frameworks have unpatched vulnerabilities, no vendor support, non-compliance with GDPR/ISO 27001. Third: business growth blocker — new requirements (mobile, API, integrations, AI) are hard or impossible to add.
Isn't it easier to rewrite everything from scratch?
The classic „rewrite vs. refactor" dilemma. Rewrites are conceptually tempting, but in practice they take 2-3 times longer than planned, the project drowns under the weight of recreating invisible business logic, and meanwhile the old system still needs to be developed (duplicated work). From our experience: 9 out of 10 successful modernizations are incremental refactoring (Strangler Fig pattern) — gradually replacing pieces of the old system while preserving business continuity. A rewrite only makes sense for very small systems.
Does modernization require business downtime?
In the vast majority of projects, no. We use patterns that allow swapping components „live": blue-green deployment, feature flags, dark launches, running old and new code in parallel with result comparison (shadow mode). Short service windows may be needed for database migrations with significant schema changes, but we plan them in advance (typically at night, on weekends) with a full rollback plan.
How long does a typical modernization take?
It depends on scale. A single monolith module carved out as a microservice: 1-2 months. A larger modernization (5-10 modules, new database, new API): 6-12 months in 2-3 week iterations. A full enterprise monolith transformation: 18-36 months, but business value appears after the first iteration — every carved-out module immediately delivers benefits (faster changes, lower risk, better observability).
What technologies do we migrate from most often?
Most common paths: PHP 5/7 → PHP 8 or Python (FastAPI) or Node.js (Express/Fastify). .NET Framework 4.x → .NET 8 or Java/Spring Boot. Java EE (JBoss/WebSphere) → Spring Boot or Quarkus. jQuery + monolithic templates → React/Vue/Astro. Oracle DB → PostgreSQL (significant license savings). On-premise → cloud (AWS, Azure, GCP, local private cloud).
What about existing integrations with other systems?
Every existing integration is mapped in the discovery phase. The migration plan covers: preserving existing contracts (internal and external clients notice no change), introducing versioning (v1 old contract, v2 new), gradually migrating consumers to v2, then retiring v1. Full backward compatibility during migration.
How do you reduce business risk?
Five layers: 1) incrementality — we replace one module at a time, not everything at once; 2) characterization tests — before refactoring we capture the system's current behavior (capture tests), which then verify nothing got broken; 3) feature flags — new functionality is rolled out gradually (1% of users → 10% → 50% → 100%); 4) rollback plan for every deploy (<5 min); 5) hypercare after rollout (intensive monitoring 2-4 weeks).
What about the system documentation that doesn't exist?
A common legacy problem. First project stage: reverse-engineering the documentation. AI agents analyze the code, database schema, production logs, and generate: architecture diagram, endpoint list, dependency map, business process descriptions. This documentation is then verified with business people (does the process look the way we understood it from the code). Result: complete documentation before refactoring starts, useful not only for the modernization project but also for the product team.
How does modernization cost compare to maintaining the old system?
In the short term, modernization is more expensive than maintenance (refactoring investment + maintaining the old system in parallel). The break-even point (where the new system becomes cheaper to maintain than the old one) typically occurs after 12-18 months. After that the new system: costs less to maintain (fewer developers, more automation), allows faster changes (shorter time-to-market), reduces risk (better observability, more tests, isolated failures).

Let's start with an audit

A one-week technical audit: mapping the current state, identifying the most urgent modernization areas, a phased plan with concrete business outcomes in the first iteration.