In this post, I would like to share my past experience migrating large Angular applications to React, and some of the lessons learnt that (in my opinion) are applicable to migration projects in general.
When I joined the company, the tech stack of the company was based on modern technologies and was maintained for a few years. On frontend, two frontend frameworks were used: Angular (2 web apps) & React (1 web app).
At the same time, the company had the ambition to add many features in next future, therefore code reuse and developer experience are highly valuable to ensure we can achieve the ambition. One strategy to achieve that was to build a design system to be used across all applications, in order to shorten design and development time. However, using two frontend frameworks means we have to create two versions of the design system, which is not a good use of effort.
Although there was only one web application using React at that time, based on discussions with the engineers in the company, most engineers prefer React as they can work more productively with it. Given that I personally also have more experiences and expertise in React, I proposed to migrate all frameworks to Angular, which were approved by the engineering leader, and that’s how it get started.
After discussion, we use different approach to migrate the two Angular applications: rewrite from scratch and incremental migration.
Small App: Rewrite & Launch
One of the Angular app is relatively smaller with only about 5 CRUD screens. With some upfront communications, I rewrote the application in a long weekend with a giant PR the next monday.
- bring impact fast
- minimal management and coordination overhead
- fun, felt like my personal hackathon
- not possible with large application
Large App: Incremental Migration
For the large Angular app, we went with an incremental approach.
We did not use the rewrite approach for this app because given the amount of code in the project, rewriting from scratch means we have to pause development for the application, which would block ongoing development & launching of features.
The plan is to :
- Migrate one route at a time by mounting a React component in a Angular component (see code snippet below). Cypress tests are usually created during this step to test against page in legacy Angular implementation and then against the page re-implemented with React.
- All new features in the application must be written with this React in Angular approach.
- Once most pages are migrated, a final rewrite of the routing with the root component from Angular to React.
Unfortunately, the migration of the large Angular app was not done until the day I left the company. Many pages have been migrated to React but many are not.
Although we achieved the goal of reusing a single implementation of the design system in all web applications, the application is left with the ugly state of using two frontend frameworks.
Since the application is only used internally, the large bundle size may not have direct business impact, however, from engineering perspective it is still a tech debt to solve due to:
- adding a new route still requires adding an Angular component
Lessons Learnt and Possible Alternatives
Looking back, although I’m still proud of what I’ve accomplished in the migration, sometimes I still wonder what I could have done differently so that the migration can be done.
Things that I’m happy with:
- Went with rewriting the smaller app and completing it in a short time.
- Came up with the incremental migration approach to achieve business goals fast.
Following are some things that I wish I can do differently:
- Realize that the migration has achieved most of its goal from the business perspective (code reuse and UI consistency), and therefore requires more push and attention from engineering (i.e. me as the owner of the project).
- “React in Angular” may not be the best approach because new features still require some bootstrap code with Angular. Instead, an alternative approach is to serve the Angular app at subpath (say
/legacy/) by building with base href and then mount React in the root (
/). With this approach, new feature no longer need to touch any Angular code anymore.
- Setting up progress tracking. Progress tracking that get updated automatically is important as it provides instant feedback to whoever works on the migration. This is somehow related to the item 2 above, the “React in Angular” approach makes the migration tracking challenging as legacy code in Angular and new code in React are interleaved together. If we went with the subpath approach, progress tracking can be simply counting the number of files of Angular code.
Although Angular and React are both mature frontend frameworks that are scalable, sometimes it is valuable to migrate from one to another in order to consolidate tech stack. For relatively small projects, rewrite from scratch is usually the preferred approach as it has the least overhead. For larger projects, incremental migration is probably the only option without impacting business operation, however, extra care should be given to avoid the migration from stalling.