How Do You Migrate a Legacy Application?
You migrate a legacy application in three phases: assess complexity and risk, select a migration strategy that matches your codebase health and business constraints, then execute incrementally with validation at every step. The most reliable approach is the Strangler Fig pattern — building new functionality on the target platform while migrating existing features one module at a time, so the application stays in production throughout the transition.
Legacy app migration is one of those projects that every engineering leader knows they need to do but nobody wants to start. The codebase works. Users do not complain — yet. And the cost of staying on the current framework is invisible until it suddenly is not: a failed compliance audit, a security vulnerability with no vendor patch, three senior developers leaving in the same quarter because they do not want to maintain a framework that reached end of life two years ago.
After 19 enterprise migration projects — most of them Angular applications, but spanning multiple frontend frameworks — the pattern is always the same. The technical migration is predictable. The organizational and planning failures are what kill projects. This playbook covers the universal patterns that apply regardless of which legacy framework you are leaving.
If you are specifically dealing with Angular version upgrades, the Angular upgrade guide covers the version-by-version path. If you need to understand whether your situation is urgent, the Angular EOL timeline breaks down which versions are still supported and which are not.
Why Legacy Applications Become Migration Emergencies
Legacy app migration rarely happens proactively. In my experience, the trigger is almost always external: a compliance deadline, a security incident, a critical hire who declines the offer after seeing the codebase, or a vendor announcement that support is ending.
The problem with waiting for the trigger is that it compresses your timeline. A migration that could take nine months with careful planning becomes a six-month rush with corners cut. The business case for migration covers how to quantify these risks before the trigger arrives — turning a reactive scramble into a planned initiative.
Three factors make legacy applications increasingly expensive to maintain:
Security exposure compounds over time. Every month past end of life adds new vulnerability disclosures with no vendor patches. Your team either patches manually, implements workarounds, or accepts the risk. All three options cost more than staying current would have.
The talent pool shrinks. Developers who know legacy frameworks retire, move on, or command premium rates. New graduates learn current frameworks. The hiring gap widens every year, and application maintenance becomes dependent on an increasingly small group of specialists.
Enterprise application security requirements tighten. SOC 2, PCI-DSS, ISO 27001, and similar frameworks all require running supported, patched software. End-of-life dependencies are audit findings that escalate with each review cycle.
Note
The cost of legacy app migration is always compared against the cost of the migration itself. But the right comparison is the cost of migration versus the compounding cost of not migrating over the next 3-5 years. When you model it that way, migration is almost always the cheaper option.
Phase 1: Migration Complexity Assessment
Before selecting a strategy or estimating a timeline, you need an honest assessment of what you are dealing with. Every failed migration I have seen skipped or rushed this phase. Every successful one invested two to four weeks in it.
The Migration Complexity Scorecard
Score your application across four dimensions. Each dimension is rated 1-5, where 1 is low complexity and 5 is high complexity.
Dimension 1: Codebase Scale
- Under 25,000 lines of code: 1
- 25,000-75,000 lines: 2
- 75,000-150,000 lines: 3
- 150,000-300,000 lines: 4
- Over 300,000 lines: 5
Dimension 2: Dependency Health
- All dependencies actively maintained, no conflicts: 1
- Minor version drift, fewer than 5 outdated packages: 2
- Significant drift, 5-15 outdated packages with some conflicts: 3
- Major drift, unmaintained packages, fork dependencies: 4
- Deep dependency rot, packages with no modern equivalents: 5
Dimension 3: Test Coverage
- Above 80% coverage with reliable tests: 1
- 60-80% coverage, mostly reliable: 2
- 40-60% coverage, some flaky tests: 3
- Under 40% coverage or heavily mocked: 4
- No meaningful test coverage: 5
Dimension 4: Architectural Coupling
- Clear module boundaries, no circular dependencies: 1
- Mostly clean boundaries with some cross-module coupling: 2
- Moderate coupling, shared state between modules: 3
- Tight coupling, changes cascade across multiple modules: 4
- Monolithic architecture, no meaningful boundaries: 5
Interpreting the Total Score (4-20):
- 4-8: Low complexity. Standard migration, 2-4 months with a small team.
- 9-12: Moderate complexity. Phased migration, 4-6 months with planning.
- 13-16: High complexity. Significant investment, 6-12 months with dedicated resources.
- 17-20: Critical complexity. Major initiative, 12+ months. Consider whether a rewrite is cheaper than incremental migration.
The Angular Modernization Checklist provides a more detailed diagnostic for Angular-specific projects, but this scorecard works for any legacy frontend framework.
Warning
Do not skip the assessment phase to save time. In enterprise projects, the assessment consistently saves more time than it costs by surfacing hidden complexity — circular dependencies, untested code paths, undocumented integrations — before they become mid-migration surprises.
Phase 2: Strategy Selection
Your complexity score and business constraints determine which migration strategy fits. There are three primary approaches, and choosing the wrong one is the single most expensive mistake in legacy app migration.
Strategy 1: Strangler Fig (Incremental Replacement)
The Strangler Fig pattern, named by Martin Fowler, is the most reliable strategy for enterprise legacy app migration. You build new functionality on the target platform while incrementally migrating existing features. A routing layer or facade directs traffic to either the legacy or modern system based on which features have been migrated.
When to use it:
- Complexity score 4-16
- The application must remain in production during migration
- Feature development needs to continue alongside migration
- The team needs time to learn the target framework
How it works in practice:
- Deploy the target framework alongside the legacy application
- Route new features exclusively to the target platform
- Migrate existing features one module at a time, starting with the lowest-coupling modules
- Validate each migrated module before moving to the next
- Decommission the legacy application when all traffic has been redirected
In Angular projects, this often means running a modern Angular application alongside an AngularJS application using a hybrid bootstrap, or routing between independent applications at the infrastructure level. The Angular modernization framework covers how to evaluate which modules to migrate first based on business impact and technical coupling.
Timeline: 6-12 months for medium enterprise applications. The advantage is that every two weeks produces a working deliverable — there is no point where the project is "half done and useless."
Strategy 2: Big Bang (Full Rewrite)
A big-bang rewrite replaces the entire application at once. The legacy system runs in production while the team builds the replacement on the target platform. At a predetermined date, you switch over.
When to use it:
- Complexity score 17-20 with deep structural problems that make incremental migration impossible
- The existing architecture is fundamentally incompatible with the target (for example, AngularJS to modern Angular, which are architecturally different frameworks)
- The application requirements have changed so significantly that rebuilding makes more business sense than migrating
When NOT to use it:
- You are several versions behind but the architecture is sound (that is a migration problem, not a rewrite problem)
- The team estimates it will "only take six months" (it will take longer)
- You want a clean start for emotional reasons rather than structural ones
Timeline: 12-18 months for enterprise applications. The risk profile is high because there are zero deliverables until launch day. Why most Angular migrations fail documents the specific patterns that cause big-bang rewrites to go over budget and over schedule.
Warning
Of the 19 enterprise migration projects I have led, zero used a big-bang rewrite. Every successful migration was incremental. The big-bang approach is listed here for completeness, but it should be your last resort, not your first instinct.
Strategy 3: Incremental Version Upgrade
If your application is on a supported framework but several versions behind, you may not need a migration strategy at all — you need an upgrade strategy. This applies when the framework itself is the same but the version gap has grown large enough to create risk.
When to use it:
- Complexity score 4-12
- The framework is the same (for example, Angular 14 to Angular 20, not AngularJS to Angular)
- The version gap is 3-6 major versions
- The codebase architecture is reasonable
How it works:
Upgrade one major version at a time, validate at each step, and repeat. In Angular, this means running ng update sequentially — the CLI enforces one-version-at-a-time upgrades and provides migration schematics that handle many breaking changes automatically.
Timeline: 1-2 weeks per major version for well-structured applications, 2-4 weeks per version for complex ones. A five-version jump typically takes 2-4 months.
The Angular upgrade guide covers the specific breaking changes and dependency requirements for every version from Angular 14 to 22.
Wondering where your Angular app stands? Take the free 3-minute modernization scorecard →
Phase 3: Execution
Strategy selected, assessment complete — now you execute. The execution phase is where planning meets reality, and the teams that succeed are the ones that build feedback loops into every step.
The Two-Week Delivery Cycle
Regardless of which strategy you chose, structure the migration in two-week cycles. Each cycle has three stages:
- Migrate. Move one module, feature, or component to the target platform.
- Validate. Run automated tests, perform manual regression testing, verify that the migrated piece integrates correctly with the rest of the application.
- Ship. Deploy the migrated piece to production. If something breaks, roll back. The scope is small enough that rollback is safe.
This cadence accomplishes two things. First, it produces visible progress that maintains stakeholder confidence. Second, it surfaces problems early — a dependency conflict discovered in week two is far cheaper to resolve than the same conflict discovered in month eight. For Angular migrations specifically, AI pair programming can dramatically accelerate the migrate-validate cycle by handling routine code conversion patterns while developers focus on architectural decisions.
Web App Maintenance During Migration
One of the most common questions from engineering leaders is whether feature development stops during migration. It does not have to.
The 70/30 model works consistently across enterprise projects: dedicate 70% of engineering capacity to migration work and 30% to feature development and web app maintenance. Feature velocity dips during migration but never stops entirely. Teams that equip developers with effective AI development tools during migration consistently protect that 30% feature capacity without burning out the team.
This model requires the Strangler Fig or incremental upgrade strategy. Big-bang rewrites, by definition, freeze feature development on the legacy system because you are building a replacement, not evolving the existing application.
Dependency Migration Order
Migrate in this order to minimize cascading breakage:
- Shared utilities and services — the foundation that everything else depends on
- Data access layers — API clients, state management, data models
- Low-coupling feature modules — features with minimal dependencies on other modules
- High-coupling feature modules — features deeply integrated with the rest of the system
- Core application shell — routing, authentication, layout
This order ensures that when you migrate a feature module, its dependencies have already been migrated and validated.
Tip
Start with the module that has the fewest inbound dependencies — the one that other modules do not depend on. This gives your team a low-risk first migration that builds confidence and establishes patterns for the rest of the project.
Phase 4: Validation and Cutover
The final phase is where the legacy system gets decommissioned. This is straightforward if you have been validating at every step, and terrifying if you have not.
Validation Checklist
Before decommissioning any legacy component:
- All automated tests pass on the migrated version
- Performance benchmarks meet or exceed the legacy baseline
- Error rates in production are equal to or lower than the legacy system
- All integration points with external systems have been verified
- Rollback procedures have been tested and documented
Enterprise Application Security in the New System
Migration is an opportunity to improve your enterprise application security posture, not just maintain it. As you migrate each module:
- Update authentication and authorization patterns to current best practices
- Replace deprecated security APIs with their modern equivalents
- Add security headers and content security policies that legacy configurations may have lacked
- Ensure all dependencies in the new system are actively maintained and receiving security patches
A successful migration should leave you with a codebase where npm audit (or your equivalent) returns zero critical vulnerabilities — something that is typically impossible on an end-of-life framework.
The Parallel Run
For critical enterprise applications, run the legacy and modern systems in parallel for two to four weeks before full cutover. Route a percentage of traffic to the modern system, compare outputs, and verify that behavior matches. Gradually increase the traffic percentage until the modern system handles 100%.
This approach adds time to the migration but dramatically reduces cutover risk. For applications that handle financial transactions, healthcare data, or other high-stakes workflows, a parallel run is not optional.
Migration Timeline Estimation
Use the complexity score from Phase 1 combined with your chosen strategy to estimate timelines:
| Complexity Score | Strangler Fig | Version Upgrade | Big Bang |
|---|---|---|---|
| 4-8 (Low) | 3-4 months | 1-2 months | Not recommended |
| 9-12 (Moderate) | 5-8 months | 2-4 months | Not recommended |
| 13-16 (High) | 8-14 months | 4-8 months | 12-18 months |
| 17-20 (Critical) | 12-18 months | Not viable | 14-24 months |
These estimates assume a dedicated team of 2-4 engineers. Scale down for smaller teams, but note that migration projects have coordination overhead that limits the benefit of adding more people.
Note
Add a 20-30% buffer to any timeline estimate. In enterprise projects, the most common source of timeline overrun is not the migration itself but the unexpected integration issues, undocumented business rules, and third-party library incompatibilities that only surface during execution.
The Patterns That Apply Everywhere
While this playbook uses Angular as the primary case study, the patterns are universal. Whether you are migrating from AngularJS, an older React version, Vue 2, Ember, Backbone, or any other legacy frontend framework, the same principles apply:
- Assess before you plan. Score complexity across codebase size, dependency health, test coverage, and architectural coupling.
- Select strategy based on data, not preference. The Strangler Fig pattern is the default for good reason — it is the lowest-risk approach for production applications.
- Execute in two-week cycles. Every cycle produces a working deliverable. No cycle should leave the application in a broken state.
- Validate continuously. Automated tests, performance benchmarks, and production monitoring at every step.
- Decommission deliberately. The legacy system is not gone until it is gone — parallel runs, traffic shifting, and verified rollback procedures.
The teams that follow these patterns complete their migrations on time and on budget. The teams that skip the assessment, choose the wrong strategy, or execute without validation cycles are the ones whose migrations fail.
Start With an Assessment
If you are considering a legacy app migration — or if you have been putting one off — the first step is always the same: assess what you are dealing with. Not a plan. Not a timeline. A structured assessment that scores your complexity and identifies the specific risks in your codebase.
The Angular Modernization Assessment scores your project across five dimensions in under three minutes. Even if your legacy application is not Angular, the assessment framework demonstrates the diagnostic approach that should precede any migration decision. Use it as a model for evaluating your own stack, or take it directly if Angular is what you are migrating.
Take the free assessment and replace assumptions with data before your next planning conversation.