Like many web apps developed in the 2010s, Leapsome is built upon the MEAN stack: MongoDB, Express, AngularJS, Node. These elements provided us with a battle-tested, solid foundation upon which we developed our product from the ground up.
In 2014, Google (infamously) announced its intention to rewrite the Angular framework from scratch. This decision didn’t contemplate a clear migration path from AngularJS 1.x to Angular 2. Consequently, AngularJS entered LTS mode in 2018; this means no new features are added, and only critical security patches may be released. As it happens, the end-of-life for AngularJS is expected by June 30, 2021.
As stereotypical as it may sound in web development, we decided to migrate our code. We wouldn't want to be stuck with an unmaintained framework, so we started looking for frameworks alternatives for our frontend last year.
Balancing benefits and risks
Code migration is no simple task. This process is often risky and time-consuming — time that could put into building even more awesome features!
In our case, besides being a technical imperative to move away from a soon-to-be-obsolete framework, the migration opened doors to opportunities. The most compelling ones are increased performance, productivity (a newer solution would be based on the latest tooling), and recruitment appeal (developers usually prefer working with fresher technologies).
To further minimize risks during our migration, we decided on the following approach:
- The migration should be gradual, as we still need to run and maintain the legacy code;
- Chosen tools and processes should withstand the test of time (in the frontend world, that’s 3-4 years tops);
- The migrated code should provide a solid foundation for further improvement (PWA, a11y, performance, SSR, Electron, etc.); and
- There should be no noticeable UI/UX difference for our end-user.
Taking into account the constraints above, and after thoroughly evaluating over a dozen frameworks and languages (yes, including your favorite one), we concluded that Vue presented the most natural evolution path for our codebase.
We had initial doubts about Vue 3, mostly concerning a potential déjà vu regarding the AngularJS situation back in 2014. Still, the backward compatibility of Vue 2 components was promptly addressed, and as it stands, all Vue 2 code will work with Vue 3 straight away.
To acquaint ourselves with the new framework, we first used Vue to build our Praise Wall. The experience was overall quite positive and gave us further confidence in our decision to migrate all of our frontend from AngularJS to Vue.
In our case, there was no hard requirement to maintain a Single Page Application (SPA) while migrating. This allowed us to take a different path from other migration processes.
To perform a rolling migration, we needed to ensure that AngularJS and Vue could coexist. For that, we relied on a hybrid frontend architecture able to seamlessly serve the right code depending on the route, without the user noticing.
Accordingly, we defined a new /v/app route alongside /app/ and swapped routes under the hood. While doing so, we made sure to keep track of which route would be served by each app.
As with any other refactor, we needed a strong set of tests to successfully migrate our frontend code. That made end-to-end agnostic tests with Cypress a crucial part of our migration effort.
Benefits and drawbacks of this approach
As with every decision, our approach to migrating our code from AngularJS to Vue has advantages and pitfalls.
A pro was starting anew with our frontend, with modern tools and a completely independent toolchain. This meant we could easily add the necessary compilation steps and have a self-contained frontend codebase.
The clear separation between AngularJS and Vue codebases also empowered us to experiment with different approaches within the new Vue code. At the same time, we ensured our legacy code was still working as expected and that it could be improved and operated independently.
This approach’s major disadvantage is that we end up serving two apps to our users, breaking the SPA contract that most modern frameworks rely upon to build rich web experiences. Yet, we managed to make the transition fast enough as not to impact the user experience. Although this wasn’t a major hindrance in our case, not every product can successfully navigate such constraint.
Keep in mind that, in this case, migrating means rewriting the entire route. This can be both a drawback (a full screen needs to be migrated at once) or a benefit, as it avoids hybrid views from intertwined AngularJS and Vue components.
Having started on this path a few months ago, we’ve already successfully built a completely new module entirely in Vue (namely, our Learning module). Furthermore, we’ve moved most of the admin sections to the new framework, while still delivering outstanding features to our customers.
The approach has been working well for us so far, and we’re aiming to finalize the migration before Angular’s end of life next summer.
Ultimately, every technical endeavor we undertake at Leapsome is geared toward our goal of making work more fulfilling for everyone. Being able to build high-quality features rapidly supports our purpose.
— by the way, we’re hiring!