Safe Frontend Framework Migration
Incrementally migrating AngularJS to React without breaking production.
"Legacy code is simply code without tests. Code without tests is bad code. It doesn't matter how well written it is."
The Theory
In “Working Effectively with Legacy Code”, Michael Feathers defines legacy code as code without tests—code that requires careful handling when making changes. The solution is to incrementally add tests and refactor, never attempting a “big bang” rewrite. This principle applies perfectly to frontend framework migrations.
As shown in the diagram above, the hybrid router directs traffic to either legacy AngularJS or migrated React routes, with a shared Redux store enabling gradual migration.
The Problem (Before)
The speech therapy medical system project GoTech worked with had a frontend built on AngularJS 1.x—a framework that reached end-of-life in 2022.
The Failure Modes:
- Security vulnerabilities in AngularJS couldn’t be patched without framework updates.
- The codebase had no tests—developers exercised caution when modifying critical flows like therapy session scheduling.
- New hires skilled in modern React faced challenges contributing to the legacy codebase.
- Attempting to rewrite everything at once would freeze feature development for months.
The Solution (After)
I led an incremental migration strategy that allowed both frameworks to coexist during the transition.
- Interop Layer: Used
ngReactto embed React components inside AngularJS templates. New features written in React from day one. - Shared State: Framework-agnostic state management using Redux, accessible from both AngularJS services and React hooks.
- Route-by-Route Migration: Migrated entire routes incrementally—low-risk pages first (settings, profiles), then critical flows (scheduling, payments).
- Test Harness: Added Cypress end-to-end tests before migrating each route, ensuring behavior preservation.
Impact Metrics
| Metric | Before | After |
|---|---|---|
| Framework | AngularJS 1.x | React 16 |
| Migration Approach | Big bang | Route-by-route |
| Downtime | Risk | Zero during migration |
When To Use This Pattern
Use incremental migration when:
- The legacy system is too large or critical for a “big bang” rewrite
- Business can’t pause feature development for months
- You need to reduce risk by proving the new stack incrementally
- The team has mixed skills (some know legacy, some know modern)
Key success factors:
- Establish a shared state layer accessible from both frameworks
- Add end-to-end tests before migrating each route
- Start with low-risk pages to build confidence
- Never break production—if migration fails, the old route still works
The Outcome
- Zero Downtime: Production was never broken during the 6-month migration period.
- Developer Velocity: New features shipped 50% faster once developers could use modern React patterns.
- Maintainability: The migrated codebase had 80% test coverage, transforming “legacy code” into “tested code.”