Migration of Legacy AngularJS App

The migration of a legacy AngularJS app to a modern framework can present many challenges. One particular case study involved a company that needed to update its app to improve performance and add new features. By carefully planning the migration process, we were able to minimize downtime and successfully transition to a newer, more efficient framework.

March 24, 2023
Every team that has apps built in AngularJS, at some point made decisions on migration. Why? Simply because it’s a legacy framework now. Initially launched in 2010, its official support ended in 2021. The choice is rather simple; Either keep maintaining the angularJS app yourself indefinitely, which, as time passes, would become even more difficult, Or Start the migration.

Migration is not a journey you set on to enjoy but it’s rather filling once you make it. We are also in mid of one such journey. We have one very successful app built in AngularJS, It’s been 7 years now since we started it. About a year and a half back we initiated talking about migration. There were a lot of concerns about budget, time, need, and feasibility as the app is very big but it didn’t take us long to realize that it’s something that has to be done for the future of the app.

Objective

Migrate the app to a rather modern framework, Be it the latest releases of Angular or its competitor like React/Vue. And do it while the legacy application is continuously evolving in parallel. One other key goal is to make it not so obvious for the end customer.

Key Challenges

Some of the key challenges we had were:

  • Application is a big monolith that has a dependency on a lot of third-party packages. Many of them are rather outdated.
  • The application is evolving almost daily. Bug fixes, new features, and UX enhancements were released very frequently. So it wasn’t an application that was done and locked. We had to redo the application and at the same time catch up with the new development that was happening around it.
  • Our team was small. It was very difficult to have a dedicated team, almost of the same size as the one working on the legacy app to take up the migration. This was something that had to be handled by the existing team only with the addition of a few team members.
  • And the biggest of them all – no test coverage. We had this big giant app with no code coverage at all.

Train of thoughts

Once we set our minds for the migrations we had the following key decision to make:

  • Should we stay with Angular or move to React? Our team was efficient with both.
  • Should we build a totally new app or somehow migrate the existing one piece by piece?
  • Should we go all-in for migration or rather adopt a slow and steady approach to migration?

The first decision that we were able to make was that we can’t build a new app in parallel because of the following reasons:

  • We don’t have a big enough team.
  • The legacy app is heavily growing and we’ll just be playing a catchup game that could go on for years.
  • And due to missing automated testing, it would be very risky to build a new app, test the whole app and release it all at once.

One module at a time strategy

Once the idea of the New App got invalidated, we know we had to go with an approach of migration that would allow us to migrate one module at a time. Here’s how it would work:

  • Choose a module & migrate it.
  • Do test coverage & get it manually tested as well.
  • Roll it out. Roll it back if something disastrous happens.
  • Remove the old legacy module so that all future development happens in the migrated one.

Once we have all the modules migrated to the new technology we’ll just be left with a shell AngularJs app that possibly is just providing Route functionality. And at that point, we’ll able to throw that away as well and completely migrate to the new technology.

React Or Angular

The last piece of the puzzle was to choose a framework. However, that was made easier by the above decisions. We were going to choose a framework that would make it simpler for us to do continuous migration.

Angular is a complete rewrite from the same team that wrote AngularJS. It’s technically a different framework honestly. Angular is written with top-layer architecture. That meant we have to load the full application in Angular if we wanted to load a single Angular component.

However, react is designed as component-based architecture. It doesn’t require the full page/app to be in react. We could easily have a part of the page in react irrespective of which framework the rest of the page is built in.

So our decision was a rather obvious one at that point and that is React. If we were migrating from Angular 2 to the latest Angular version, it would have been a different story.

Solution

Once we had all the key decisions made, we had some rather technical points to deal with i.e how to share data between React Components and AngularJS app, Deployment processes, etc. Below is the list of rather small decisions we had to make.

  • React with kept in a different Repository and had a different build release life cycle. JS/CSS would then be loaded into the AngularJS app. This also meant we had to customize the webpack file in our react app a little to have more control over the bundling process.
  • For Interaction between AngularJS & React, We had to develop a module each in React & AngularJS

Here’s a high-level diagram explaining the concept:

High-level diagram explaining the concept
Here’s all the code we had to write to plugin both AngularJS and React together.

React Code:

 window.ReactApp = {  
     loadComponent: function(name, elementId, outData) {
         const Elem = { 'Signup':  }
         const wrapper = document.getElementById(elementId);
         wrapper? ReactDOM.render(elem[name], wrapper) : false;
     }
 };
AngularJS Part:

var Dir = function ($injector) {
    return {
        restrict: 'A',
        scope: { refData: '=' },
	controller: ['$scope', '$element',
	    function($scope, $element) {
	        // @default method ( init )
	        var init = function() {
                    var compId   = $element.attr('id');
                    var compName = $element.attr('jp-React');

                    if( !System.haveValue(compId) || !System.haveValue(compName) ) { return; }

                    if( System.isObject(window.ReactApp) ) {
                        window.ReactApp.loadComponent(compName, compId, $scope.refData);
                    }
	        };
	        
                // Call  the init method when the controller is loaded
	        $injector.invoke( init );
        }],
    };
}

// Dependencies Injection
Dir.$inject = ['$injector'];

// Angular Module & Directive Initialization
angular
.module('module name)
.directive('projectNameReact', Dir)
Below is the code to include the React Component in the AngularJS app:

<div id="signup" project-name-React="Signup" ref-data="Ctrl.refData"></div>

Result

With this approach, we were able to pace the migration to our capacity and at the same time lower the risk. It’s been about a year now and we have managed to migrate all the major modules to react risk-free. It’s still only about 50% of the app. But the approach has proven itself and we are confidently moving toward a migrated app in react.