Look, I get it. Migrating from AngularJS to Angular is not an easy task. Depending on the size and complexity of the application, we’re talking about a large scale refactor that could take more than a year. I know because I’ve been there, and it’s why I built my comprehensive video course UpgradingAngularJS.com.
Just like any big software project, migrating to Angular requires a plan. The last thing I want for you is to get halfway through a migration and realize there’s a critical problem that forces you to have to scrap the whole thing. We’re going to prevent that by looking at where you should focus your time in preparing for your upgrade. A good plan will not only help you finish the migration, but also save time and money.
(P.S. I’ve written elsewhere about how to decide if you should stick with AngularJS, switch to a different framework, or migrate to Angular. In this article, we’ll assume that you’ve decided to make the leap.)
First, you need to look at your team. After all, they’re the most important part of this process. Of course, the size of your team makes a difference. How many people will be working on this migration?
Next, what about capacity? Will they be working exclusively on this technical debt, or also on feature development? Keep in mind, the migration process can be tedious and frustrating at times. Build in buffers to prevent burnout.
You also need to take into account your team members’ expertise and experience. If your team is mostly experienced with AngularJS, and not familiar with the Angular framework and concepts like reactivity with RxJS, you’re going to need to build in some time for learning curve. You’ll also need to account for any sort of training your team may need to pull off this refactor, or pair programming sessions to help junior developers get up to speed.
For training, in addition to my course UpgradingAngularJS.com for ngUpgrade, I’d also recommend Angular-specific resources like ng-book, Todd Motto’s Ultimate Angular courses, Ben Lesh and Tracy Lee’s RxWorkshop, the many Angular Pluralsight courses, and in-person training from Oasis Digital or Angular Academy. Angular and RxJS do have a learning curve, and there’s no sense in trying to figure them out on your own when there are so many experts out there ready to help.
Once you’ve got your team on board, you’ll need to look at five layers of your application structure to determine your timeline and the type of migration you’ll be doing. Each will give more shape and definition to where you’re starting in the process and how to proceed.
Adherence to Style Guide
You’ll first need to determine how closely aligned with the AngularJS style guide your app is. Are your files organized by feature? Are you keeping a single item in each file (as opposed to a controller and a service in one file, for example)? Are you wrapping your functions in IIFEs?
None of these are deal breakers, nor should you feel obligated to do a lot of refactoring ahead of time, but your answers to these questions do affect your migration timeline. If your app is still using outdated practices, you’ll need to take that into account when adhering to the new conventions adopted in Angular. Chances are, you’ll also need to re-learn some patterns along the way.
Next, you need to look at three categories of your dependencies, which are perhaps the most complex part of an upgrade.
Version of AngularJS
Which version of AngularJS are you using? If you’re using AngularJS 1.4 or below, you’ll need to update to at least 1.5. Ideally, update to version 1.7.x, which will be the final release of AngularJS. Updating versions of AngularJS is usually easy, but it’s a good idea to update one version at a time while using the official AngularJS migration guide. Version 1.5 adds the component API, which is the first introduction to some new Angular concepts and allows for a smoother transition.
Third Party Libraries
I’ve often found that things that were very complicated in AngularJS and required an extra library are much simpler now in Angular. The front end evolved quite a lot in the years since AngularJS first came out, and Angular has these best practices built right in. Combined with the reactivity that RxJS brings to the table, you may find yourself abandoning more external libraries than you think.
Internal Shared Libraries
Are your applications connected by shared libraries or components? If so, you’ll need to come up with a separate plan for dealing with those. Generally, you can use ngUpgrade to “upgrade” or “downgrade” these, or you can write second versions in Angular. There are pros and cons to both. If you choose to use ngUpgrade to “bridge the gap,” you may run into unforeseen problems with change detection (particularly with regard to testing) or feature compatibility. It’s not a frequent occurrence, but it’s something to be aware of. Of course, writing a second version of a shared library means duplicated code, which means twice as much maintenance during the upgrade. At the same time, sometimes it’s just much more straightforward to rewrite something in Angular rather than to try to make the AngularJS code available to Angular.
Up next is your app architecture. Are you taking advantage of component architecture instead of using
$scope, controllers, or element directives? If you are, you’ll save time in your migration. Converting AngularJS components to Angular components is largely pretty easy. If you’re not, you don’t need to do this conversion ahead of time. Just know that you’ll have to do some untangling to get from those old data structures to components.
Similar to internal shared libraries, identify any components, attribute directives, filters, and services that are shared throughout the application. Identify what they do, where they are used, and the impact of either downgrading or duplicating them. Again, you may find that it’s simpler to maintain a version in each framework for a while (and you’ll have to for attribute directives and filters, which cannot be upgraded or downgraded with ngUpgrade).
The other piece of architecture is modules (Angular modules, not ES6 – more on that in a minute). Is your app organized into feature modules, with perhaps a core or shared module as well? In AngularJS, admittedly, these were largely for the sake of organization. In Angular, though, modules make a big difference. You have to explicitly import or export shared items, and modules also have the ability to be lazy loaded. If your AngularJS code is already divided into modules, it allows you to approach your migration with better separation of concerns. You can migrate one module at a time, as well as any of its shared items, while keeping the rest of your legacy code separate. Again, if your app is not already broken into modules, you can do this as you go.
Your Build & Tooling
While dependencies are definitely the gnarliest part of migration, the most difficult piece – but also the most rewarding – is the move to TypeScript, ES6 modules, and a build process. Getting your code switched to TypeScript and your build set up correctly for your upgrade can be frustrating, but will pay off exponentially when done right.
You have two choices: the Angular CLI or a custom Webpack setup. Assuming you’re doing a gradual rewrite with ngUpgrade, either of these will require you to first replace your script tags or task runners (like Gulp or Grunt) and move your application to ES6 modules and TypeScript. This can feel like an insane amount of work, so to combat this I recommend doing it all at once. Pause your feature development, create a branch, and take a sprint to migrate the codebase. You can leverage PowerShell or Bash scripts to assist in the bulk file renaming, and then use Visual Studio Code to work your way through any issues with TypeScript.
Once you’ve moved to ES6 modules and TypeScript, it’s time to set up a build process. Using the CLI is ideal, but is often difficult for big AngularJS apps that are not ready to move to such an opinionated folder structure and build process. This is getting less common as the CLI continues to evolve and become more flexible through features like schematics. If that’s the case for you, though, you’ll need to set up a Webpack build that can be used in both development and production. You can then switch to the CLI later, once your app architecture more closely resembles the style of a CLI project.
Whichever you choose, allow yourself to take the time to get it right. You’ll want your legacy AngularJS code to run alongside of your new Angular code, and you’ll need to deploy it to production without breaking anything and without bloating your bundle size. It may feel like a lot of work on infrastructure, but it’ll pay off in the long run as you’re able to simultaneously maintain and migrate your legacy AngularJS code while developing new features in Angular.
I’ve got good news and bad news for you about testing during ngUpgrade. The good news is that you may find that the TypeScript compiler eliminates a great deal of your unit tests. I’ve encountered quite a few AngularJS unit tests that were basically just testing that dependency injection was working and everything was functional. The other good news is that the tooling is the same. You can still use Karma and Jasmine for your unit tests (though you can switch to Jest if you’d like).
Now the bad news. While testing in Angular after the migration is well-defined, testing during ngUpgrade in the real world can be difficult. This is due to the way the AngularJS and Angular change detection and dependency injection systems overlap. You should be able to set up Karma to run your pure Angular and AngularJS tests (and a pro tip here: use two separate Karma config files). However, once ngUpgrade enters the mix, things get weird. As it stands now, components and services that mix the two frameworks (e.g. Angular Component A uses upgraded AngularJS Service B) are well nigh untestable.
The problem here is that the dependency injection systems aren’t talking to each other correctly during testing. Currently, the only way to get around this is to manually spin up mini Angular and AngularJS modules in each test, as demonstrated in the ngUpgrade source code. If you look at that snippet, you’ll see that a huge amount of setup is required for a single assertion. While there is an open PR for some test helpers to be added, it’s unclear when this will be merged in and made part of the official ngUpgrade story.
There are similar problems with end-to-end testing, mostly revolving around change detection. While the Protractor team has done a great job of adding hybrid support, there is a significant bug involving third party libraries that use
$watch in an ngUpgrade application. Personally, I ended up having to switch to Cypress, as it is framework agnostic. However, that switch did require rewriting end-to-end tests, and that’s not always feasible.
These testing problems don’t affect all applications, and to be clear, they only happen while the app is in hybrid mode. Once the app is fully converted to Angular, you won’t have these problems. I recommend that you take stock of how critical your unit and end-to-end tests are to you during the migration. For some companies, it may not be a big deal, or it may be that the migration timeline is short enough as to not matter. This will also affect how you migrate. Instead of relying heavily on upgrading and downgrading services, you may want to rewrite entire features at a time so that your tests will continue to run.
If that’s not an option for you, then I’d recommend rewriting your tests as you go without trying to get them running, and migrate as quickly as you can all the way through to Angular. You’ll also need to heavily rely on the TypeScript compiler and UAT (user acceptance testing) with each release until you’re fully converted.
Either way, don’t get blindsided by testing if it’s critical to your business.
Putting It All Together
We’ve covered a lot of ground, so let’s put this together into a list of questions you can ask as you embark on migrating from AngularJS.
- What’s the size of the team?
- How much capacity will each team member have?
- What are their strengths and areas of expertise?
- How much time for education do we need build in?
- What educational resources do we need?
Style Guide Alignment
- Are files organized by feature instead of type?
- Is there one item in each file?
- Are we using IIFEs?
- What version of AngularJS are we using?
- What AngularJS-specific libraries are we using?
- For each one: should we find a new version, rewrite, or abandon?
- What shared internal libraries do we have?
- Are we using components instead of controllers or element directives?
- Are we using lifecycle hooks?
- What shared components, directives, services, and filters do we have?
- Are we using feature modules, shared modules, or core modules?
- Are we currently using script tags, a task runner, or a module bundler?
- Are we currently using ES5, ES2016, or TypeScript?
- Should we move to the Angular CLI or a custom Webpack build?
- Can we dedicate some time exclusively to migrating to TypeScript and Webpack/the CLI?
- What is the current state of our unit tests (quantity, quality)?
- What is the current state of our end-to-end tests (quantity, quality)?
- How critical are these tests during the migration?
- Do we have people available for user acceptance testing during the migration?
Use this cheat-sheet to determine what your timeline and risks are before you start migrating. This will also help you decide which approach is best for you: a gradual migration with ngUpgrade, a complete rewrite, running parallel applications, or (when it’s ready) migrating with Angular Elements. For example, if you find have a ton of shared services and your unit tests are critical to your business, you may want to gradually build up a parallel rewrite. On the other hand, if you could focus your time and effort into a hybrid migration, ngUpgrade is a great fit. Unless your app is very small, chances are you won’t be able to drop everything and do a complete rewrite, but you never know – the benefits of moving to Angular and the CLI are well worth the effort.
We’ve taken a dive into what you’ll need to look at before you start your upgrade. From your team to your testing, from your app architecture to your third party libraries, take stock of where you’re at in order to understand where you’re going. It may feel like you’re taking extra time away from the actual migration by being so methodical ahead of time, but resist that feeling. I’d hate for you to get derailed by a surprise discovery of a third party UI widget scattered all over your app that requires you to rewrite it from scratch. Identify that kind of thing first and build it into your plan.
If you’re overwhelmed by any of this, I’ve got your back. I’ve got 200+ detailed videos, quiz questions, and more for you in my comprehensive course UpgradingAngularJS.com. I created it for everyday, normal developers and it’s the best ngUpgrade resource on the planet. Head on over and sign up for our email list to get yourself a free Upgrade Roadmap Checklist so you don’t lose track of your upgrade prep. And, while you’re there, check out our full demo.
About the Author
Sam Julien is a Google Developer Expert for Angular, teacher, and full-stack developer. He’s also the founder of the comprehensive video course UpgradingAngularJS.com and the co-organizer of Angular Portland. When he’s not coding, you’ll find Sam outside hiking and camping like a good Oregonian. You can follow Sam on Twitter @samjulien