Migrating the MAAS UI from AngularJS to React

MAAS (metal as a service), is a Canonical product which allows for very fast server provisioning and data centre management. Around 2014, work began to build a rich UI for MAAS, primarily using the AngularJS JavaScript framework from Google. AngularJS today is in long term support (LTS) and due to reach end-of-life in 2021. This year we began the work of transitioning away from AngularJS in anticipation of this impending EOL to more contemporary tooling.

Evaluating Angular vs React

Google’s recommended upgrade path for applications built in AngularJS is to transition to the Angular framework. Despite the similarity in naming, Angular is very different from AngularJS architecturally, and the migration process is non-trivial. While components (allowing for the now ubiquitous uni-directional data architectural pattern) were later backported from Angular to AngularJS, most of MAAS UI predated this and consequently migration to Angular would require significant app-wide refactoring.

Since the inception of the MAAS UI, a number of other products had been built at Canonical using React. As we had developed significant experience using React, and tooling in the surrounding ecosystem, ultimately it made more sense to invest in transitioning the MAAS UI to React rather than Angular. This choice conferred additional benefits, such as standardising our build and testing infrastructure, and allows for component reuse across products. We also just generally enjoy working with React, and feel that the most significant developments in web UI technology are happening within the React ecosystem (hooks, concurrent mode, suspense, CRA).

Migrating to React

A number of potential approaches were considered for the migration, big-bang, in-situ and hybrid.

Big Bang

As delightful as it is to work on a greenfields project, rewriting the entirety of the MAAS UI, particularly while still delivering features and bugfixes for our supported product in one go, was simply not feasible.

In-situ

An in-situ migration would involve rendering new react components within the existing AngularJS code, using a library such as react2angular. While this may initially feel like the path of least resistance, it presents essentially the same issue as migrating to Angular – requiring that the AngularJS code is architecturally aligned with React, using uni-directional data flow and components. Significant refactoring to a codebase that we ultimately intended to replace, felt like a questionable use of time.

Hybrid

We eventually settled on a hybrid approach to migration, which involved creating an entirely new react-redux application, initially implementing a portion of the MAAS UI (settings and preferences in MAAS 2.7), and routing between the legacy and react app from Django.

UI Codebase Separation

As part of the work to transition to a hybrid application, we took the opportunity to separate the MAAS UI codebase from MAAS core. While this work shouldn’t be obvious to users of MAAS, it has provided a number of benefits to our development teams that will help us continue to build a great product. We also took the opportunity to make UX improvements to the Settings & Preferences experience as we migrated to React.

Benefits

Separation of the UI codebase from MAAS core has provided a number of benefits:

  • Better separation of concerns – MAAS core now exclusively provides APIs.
  • Faster tests – MAAS core CI is now faster.
  • Fit for purpose – build and configuration tooling can be better tailored to the needs of each project.
  • Team agility – more flexibility for both the UI and Core engineering teams.
  • Easier QA – designers are now able to point their development UI at a persistent MAAS server, rather than setting up and configuring MAAS on their own hardware.
Designers can now point their maas-ui at a remote MAAS.

Process

Before (Single repo – UI in AngularJS & Django) ⟶ After (Multiple Repos maas-ui, maas-core – UI in Angularjs & React)

The AngularJS UI was migrated from the MAAS core codebase, to a project called legacy in the maas-ui repository.

The maas-ui repository uses yarn’s implementation of monorepos, yarn workspaces, allowing us host multiple yarn projects within the same git repository with shared dependencies.

A new react application was bootstrapped using Create React App in a project simply called ui. The new ui project is a React 16 app, built exclusively on the hooks API, redux (with immer) and redux-sagas.

A third project, shared, contains components common to both legacy and ui (e.g. header, footer components).

Development Proxying

While routing between both legacy (AngularJS) and ui (React) apps is simply handled by Django in production, in development we needed a way to work on both projects seamlessly. We created a simple proxying server, using ExpressJS and http-proxy-middleware to route requests between both the legacy and ui apps. The server also correctly proxies requests to the webpack devserver to support reloading for both projects, preserving the nice developer experience we would have for a single project.


Into the Future

From MAAS 2.7 onwards, new UI features in MAAS will be developed exclusively in React. We now have an excellent foundation on which to continue delivering UX and performance improvements to our MAAS users.

If working on products like MAAS interests you, we’re currently looking for UX Designers to join our team, and may be looking for frontend engineers in the near future.

Posted in: