How We Migrated from AngularJS to React/Redux in 2 Weeks

In January 2017 when my business partner and I decided to build the MVP of our FinTech SaaS platform Systelos, I chose to build it in AngularJS. This decision was made with the understanding that the codebase would later be migrated to a newer Javascript framework.

Sure ReactVue and Angular 2+ were superior frameworks, but they were not valid options for us at the time due to the following reasons:

  • Prior to Systelos I spent two years as CTO for a VC-backed blockchain company where I did very little hands-on coding. I was excited to roll up my sleeves and write code again but I knew it would not be easy. For these reasons, I wanted a technology I was most comfortable with (AngularJS) especially with our deadline of three months.
  • AngularJS still had the largest community of developers, so I knew that finding talent would not be difficult.
  • I like to be practical and efficient, so my plan was to hack through the development process using open-source packages available and the AngularJS community was more likely to have what I was looking for.

Fast forward two years later, I find myself once again less hands-on in my role with a great team that actively develops and has prior experience with React.

Moreover, AngularJS was starting to show its limitations.

The problem

The bigger our codebase got, the harder it became to maintain. One of our biggest problems was that every ng- attribute{{expression}} and $scope.$watch contributed to slowing down the $digest cycle of our application. There was only so much that we could do to reduce the number of $$watchers on our pages. To combat this we tried to remove as much two-way binding as possible, but we still ended up with thousands of watchers and a slow digest cycle, making our app fairly unresponsive and sluggish.

This, combined with some code reusability issues and the fact that AngularJS was no longer being actively developed, made me look into possibly rewriting the whole application.

Why we chose React

The decision to use React over other modern frameworks like Vue and Angular was an easy one.

  • React’s Virtual DOM would solve the performance issues we previously had. It achieves this by keeping track of changes and updating only the necessary parts of the DOM that changed without rebuilding the entire DOM.
  • Its Component structure would allow us to re-use and share components across our codebase. These components reflect a particular part of the user-interface, which would be shared across multiple pages.
  • Styled-components solve so many CSS problems I’ve experienced personally. How many times have you had to fix a CSS bug that was a caused as a result of classes overwriting each other?
  • React Native would help us write business logic once and reuse it for both mobile and web.

How we migrated

The obvious solution to migrate to React would be a complete rewrite. However, we’re a fast moving startup and couldn’t afford to stop delivering features to our customers while we rewrite everything. Instead we decided on a progressive migration strategy, where new features would be written in React and existing features would be gradually replaced over time.

In the paragraphs below, I’ll describe the steps we took to migrate our application from AngularJS to React.

Step 1: From Gulp to Webpack

First we had to remove Gulp to start using Webpack, which integrates nicely with React. Run the following command to install Webpack.

npm install webpack webpack-cli webpack-dev-server — save-dev

Webpack requires a file (or files) to be defined as entry point. These files should require/import all the app’s dependencies and files needed to run the application. To do this, we created five files:

vendors.js — This file imports all of the dependencies (scripts only) from node_modules that the app needs.

vendors-styles.js — This file imports all of the dependencies (styles only) from node_modules that the app needs.

app-styles.js — This file imports all the LESS and CSS files from our app.

app-templates.js — This file grabs all the HTML files from our app and saves them in template cache so they’ll be available when the app is running.

app.js — This file imports all the app’s JS and JSX files and combines them with the other app files from above (HTML, LESS, CSS).

The final webpack.config file looks something like this:

Check out these articles for more instructions on setting up Webpack;

Step 2: Ditch Bower for NPM

It’s no longer recommended to use Bower since it’s being deprecated and they advise developers to move to Yarn (in our case we used NPM). So we moved the dependencies that were defined in bower.json to package.json.

However, there were some issues we had to overcome.

Issue #1: Some Bower packages have different versions and names compared to their NPM equivalents.

Solution: Find those packages manually on the NPM official site.

Issue #2: Some packages depend on other packages of the same version. For instance packages “angular-animate”, “angular-aria”, “angular-cookies”, and “angular-messages” depend on “angular”; so if those packages versions don’t match, the application will throw errors when trying to register the modules.

Solution: Figure out which packages depend on which and make sure their versions are compatible. As for the packages that depend on AngularJS, their versions need to match the Angular version the app is using.

Step 3: Setup Babel

In order for Webpack to transpile ES6 syntax — which we intend to use, babel-loader was needed.

First we installed the following packages:

npm install @babel/core babel-loader @babel/preset-env @babel/preset-react — save-dev

Next we created a .babelrc file in the root directory (where package.json is) and added the following presets:

  • @babel/preset-env for ES6, ES7, and ES8 files
  • @babel/preset-react for React Files
 “presets”: [

In the webpack.config.js file, we then needed to configure Webpack to load/transpire JS and JSX files with Babel. To do this, we added the following rule in the webpack.config.js

 module: {
 rules: [
    test: /\.(js|jsx)$/, //rule for js and jsx files
    use: ‘babel-loader’,
    exclude: /node_modules/

Step 4: Unit Test with Jest and Karma

Setting up Karma

Due to our existing tests written in AngularJS, we needed to find a way to also run the new React tests alongside. To do this, we configured KarmaJest andEnzyme.

Webpack tends to fail and throw errors when reading our spec files due to the AngularJS dependencies included. For example, a spec file with the following code will throw errors stating that the injected dependencies are undefined.

beforeEach(inject(function (_$controller_, _$rootScope_, $httpBackend, _$uibModal_, FiltersService) {…}

So we configured it to not process .spec.js files.

Setting up Jest & Enzyme

Our Jest config file looks something like this:

Our Enzyme config file looks something like this:

Results: our app is 3x faster

With every engineering project we undertake, metrics are very important to us. They help us gauge how much better we are as a team, and how many of our goals are met.

So we ran a test between two pages of our application; one written in React and the other in AngularJS. These are the results:

You can see from these results our app is much faster when written in React, mainly thanks to the Virtual DOM. It does a great job managing DOM updates compared to AngularJS which redraws the entire DOM every time small changes happen.


There is no best way to migrate from AngularJS to React/Redux — it all depends on your team, product requirements and timing. I chose a practical and efficient strategy at Systelos where we focused first on implementing the basic configuration and tooling needed to run React alongside our AngularJS app.

In my next blog post I’ll discuss how we write React components alongside AngularJS and how we’ll be progressively migrating the rest of our application. Stay tuned for part 2!

Why we ditched Unit tests for Integration & End-to-End tests

With the rise of agile development practices like continuous integration, development teams have struggled to find the right balance between unit, integration and end-to-end tests. Most developers settle for the 70/20/10 rule as promoted in this Google Testing article which splits testing to 70% unit tests, 20% integration tests, and 10% end-to-end tests.

For some spending 70% of their testing efforts on unit tests is not optimal; this was the case for our team at Systelos, a SaaS platform that allows financial advisors to guide client behavior at scale. We tried it for a few months and it didn’t work, so we ditched unit tests in favor of integration and end-to-end tests.

The problem

As a team, we wanted to be able to answer questions like is our application working the way we want it to, or more specifically:

  • Can users log in?
  • Can users send messages?
  • Can users receive notifications?

The problem with the 70/20/10 strategy which focuses on unit tests is that it doesn’t answer these questions and many other important high level questions.

Unit tests are too isolated to test real user stories. They are fast and isolate failures really well, but don’t provide the same confidence integration and end-to-end tests do. Especially with a frontend heavy application like ours, where the user’s behaviour is complex and unpredictable.

Furthermore, most issues reported in our JIRA were a result of miscommunication between multiple UI components and backend services. When incorrect information was presented to our users it was usually because a service didn’t access the required information from the right location, or because different frontend components didn’t work together properly.

Our solution

Since unit tests didn’t have much impact on the reliability of our application, we needed a better testing strategy to help us answer high level questions like, can a user still send a message after all these code changes?

After close inspection, we noticed the small amount of end-to-end tests we had did a better job of spotting issues we hadn’t seen before; they proved to be valuable in our quest for reliability. This prompted us to to flip the pyramid on its head and focus on both end-to-end and integration tests.

Our new strategy was to split our automated tests into 20% unit tests, 30% integration tests and 50% end-to-end tests. With the help of testing frameworks like Jasmine, Protractor, Sinon and Karma, this new strategy helped us catch 95% of issues before they made it to production.

20% unit tests

We still believe unit tests provide many advantages to our team; they run fast, provide great isolation for failures, and are much more reliable than integration or end-to-end rests.

Because we now only use 20% of our testing efforts on unit testing, we’re picky about which functions to test. We only focus on functions that have a high probability of breaking when the logic is incorrect or when it receives wrong inputs.

30% integration tests

Integration tests are tricky to implement especially on the frontend because larger functions tend to interact with many different services and APIs. We use Sinon.js to spy, stub and mock external services since they’ve already been tested. And just like our unit tests, we only focus on high reward functions.

50% end-to-end tests

We focus most of our efforts on end-to-end tests; they test the whole application from start to finish. They ensure that all the integrated pieces of our application function and work together as expected. They simulate real user scenarios, essentially testing how a real user would use the application.

We use Protractor to control the browser and simulate a client nudging and starting a conversation with their advisor regarding an update about a life change. Running multiple tests like these to test every single functionality within our application helps us ship code confidently.

There are however hurdles we had to overcome in order to make this possible within our continuous integration environment where code is released every week. Some of them include:

  • End-to-end tests take a long time to run. This can be a problem when you’re trying to deliver code fast (continuous delivery FTW!).
  • They sometimes crash for no reason. The browser may treat long running requests as failures, or another developer may have changed the IDs in your HTML without updating the tests.
  • They take a lot more time to write. Since user actions require multiple steps, you need to be extra careful managing your IDs, adding timeouts, simulating external devices and browser capabilities, the complexities are endless!

We took several steps to solve these issues, and I’ll be sharing them with you in my next blog post so stay tuned!


Finding the right balance to how much unit, integration and end-to-end test to write is not easy. Every team and application is different and requires a different ratio. A good way to identify the best strategy is to analyze and reflect on questions like which tests within your codebase have made a big impact? which ones saved you from having your application crash on production? For us, it was clear that end-to-end tests made the most impact and found the most bugs, with integration tests coming in second.