Migrating from Angular to Vue
How Fundbox migrates from Angular to Vue while still being able to develop roadmap features on time
This is Part 3 of the series: “State of Front-End at Fundbox”. Part 1 covers the existing infrastructure at Fundbox and our motivations for redesigning it. Part 2 presents the research behind choosing a new framework: React, Vue or Angular to migrate into. Part 3 describes how we are migrating from Angular to Vue while still committing to a demanding product roadmap.
Vue it is
We selected Vue as the framework to build our existing and future applications. That is despite the fact that React could be considered the natural selection, as it’s the most popular of the three. The decision was finally made, after a few months of research that peaked with a 4-hour meeting in which all the relevant stakeholders took part.
I personally believe we didn’t select the absolute better framework, we chose the better framework for us, with our specific blend of technologies and developer expertise. The everlasting framework battle is like the never-ending iPhone vs. Android debate. There’s no one absolute better choice; there’s a better choice for every use-case and that’s how you should tackle the framework decision for your own projects.
In multiple small internal projects and POCs inside the company we had a chance to test the waters with Vue and use it in real situations. Vue felt great. Both in how easy it is to ramp up, and also in how flexible it is.
React, being the more popular choice, has its known disadvantages (not to say Vue is innocent…) and we felt that to be able to support an easier migration process from our current AngularJS infrastructure, we want to have a framework that has everything already built-in it and is easy to grasp.
We wanted a framework that is opinionated enough, but not too much, and we wanted it to have good documentation such that it would be easy for Vue-newbies to switch between code and documentation, and find guidance quickly. To be more clear, we wanted to prevent as much as possible the need to look for answers on the web, and find too many different opinions on how to tackle common problems.
In part 2 you can read more details about how the frameworks compare.
Rewriting 6-year old front-end code
Our codebase includes hundreds of Angular-related files and more than a hundred Angular components. Our web applications uses these components as the building blocks of our interfaces.
We have at least five development teams that contribute to the components directory. Each of these teams actively develop their own user-facing products, but most of these products share similar styles, flows and components.
We considered three possible migration paths:
- All-in: Allocate a development team (or an ad-hoc task force) that will build all components from scratch. That team will be dedicated only for that task and will not take part in roadmap related features.
- Divide & Conquer: Tackle the project app by app, product by product — create a new Vue app, implement its components and flows, then move on to the next app. We have multiple apps and products and teams own one or more products’ development.
- Hybrid: Create Vue components and refactor old components one by one, load them inside the Angular apps. This way the old Angular app will be refactored from within until all components are migrated to Vue. When only the app shell is left, move all components to a new Vue app and throw away the old Angular shell. With this solution, the app experience will not suffer throughout the development process because the Vue and Angular components live together side-by-side all along.
The path not taken
The first two options share one major problem: a good amount of developers will be blocked for a long time from developing new customer-facing features as they will be allocated to working on the new Vue app. This means slower delivery of new features but also slower response time for production support.
That said, the first two paths bring much more focus into the process, as the work can be done in one (long) breath from scratch to completion so it’s easier to see its beginning and end. If you can spare a team of developers (depending on the size of your products, it might require all your developers), you might still prefer one of the two blocking paths.
Furthermore, when undertaking such a big project (basically building a 6 year-old product from scratch), it’s important to take into account unexpected delays along the way. A group of people not delivering new features every sprint, could be blocked for months, and that could come out as a hard kick in the stomach for any company, and even harder for a mature startup as we are.
Hybrid all the way!
To not halt our existing product development, and as an outcome, hurt our loyal customers, we chose the Hybrid path. The main disadvantages of going the hybrid path, is that it takes more effort to sync the work between teams as the work is spread between all teams, and that it will take longer gross time to finish the whole project.
The nice thing about our selected hybrid path is that we can always boost it with dedicated development marathons in which a group or team can allocate a sprint (for instance) to implement a page or a user flow. These boosts can cover for the slower nature of a hybrid path that we chose.
New Components vs. Refactor Old Components
So we had the path clear, but we had more things to consider:
- Infrastructure and cross-apps, cross-teams work had to be done prior to adding Vue code into the Angular apps, to lay foundations and support the new technologies we are introducing to an oldish codebase. We needed to define these infra tasks and allocate people in teams to implement them.
- Developing new components in Vue instead of Angular is “easy” (as an idea, at least). But what about refactoring old code? We have tons of Angular components that needs refactoring. How will these become tasks in the running sprints, it means going back to every component we ever developed and rewrite it.
First, we decided to write only Vue code from now on. That means — new components, that would have been tasks in sprints anyway as Angular components, will be written in Vue instead. This surely means the first few Vue tasks will be slower as developers needed to get to know Vue better.
For rewriting old components we defined a guideline to follow: if there’s a task in a sprint that requires changing more than 30% of the code of an old Angular component, we turn that task into a longer task that includes rewriting the whole component into Vue. For anything less than 30% we just make the changes in the Angular code, to not make every little task drag on too long.
The first thing we did before actually sitting to write code, was to define the boundaries of the migration process and to design the required solution. We started to plan this wide project with a series of meetings of a team we named the “Vue Committee”, which is a team of representing front-end developers from the different teams and products.
The main goal of the team was to be as focused as possible and to be able to decide fast on the many different parts of the new framework.
We use confluence to document our work processes at Fundbox. We created a dedicated “space” in Confluence for the migration to Vue. In that Space we documented meeting notes and kept track on the core decisions of the new framework that will be built with Vue.
After defining the basic properties of the framework and choosing a technology stack that fit for us, we could make the first commit of Vue code. We split some basic groundwork between the teams to be able to start developing as soon as possible.
Vue inside Angular
Our selected hybrid path, required loading Vue components inside Angular apps. We needed to bridge the two frameworks. We found ngVue to be a good solution that does just that: it is an Angular module that allows integrating Vue components inside AngularJS applications with reactive data binding.
Hence, the first code commit was adding Vue and ngVue as dependencies of the Angular app. This provided the most basic requirements for us to start running.
A word about state management
If you have a state management solution of any kind, you’d have to design work process (and possibly even write a service) that syncs state between the two. We chose to go with Vuex for state management in our new infra, but we had an easier challenge there because we only had one app that managed data through a store-like (in-house) solution, so it wasn’t as complex as an app that uses something like Redux, for instance, to manage states.
When choosing the order of components to rewrite, select them in groups that can be contained together and by that bypass the conflict of stores. Design new components in a way that won’t collide with your old framework’s states.
If your state management solution is inherent in your apps, you might have to either choose one of the blocking paths I suggested above (so that you won’t have two stores that need to live together) or first refactor your older store so that it won’t interfere with your new components’ states.
Develop components, not applications
The term Atomic Design have already been marked years ago, only to open the door for a plethora of work methodologies to support the concept. Following this practice means — developing pure, reusable components.
We use Storybook to develop our components in isolation. This way, the components are absolutely non-dependent on the app that will be using them eventually, relying solely on props that will be sent to the component from the app that renders them.
Storybook also promotes healthy work-relations between developers, designers and product managers. Storybook serves as a UI library for us, where anyone in the company can jump in to play with user-facing interfaces, or parts of interfaces — components.
This helps with making design brainstorms, as the design team can easily navigate through the existing UI components, without needing to actually get into the product, creating a demo user and playing around. Also, developers can easily find out if a component they need already exist (or at least something that is close enough to what they need).
You can check out public Storybook examples here:
CI/CD: UI library as an NPM package
Storybook is a nice interface to view and play with the available components, but it doesn’t help at all with using these components in your app code. We decided to pack our library of components as an npm package, so it could be developed and updated independent to the apps that make use of it.
Once the package is available on npm, we could import our UI library in our apps and use components freely. This required the npm package to have its own CI/CD process, so that we could publish package updates to npm, to eventually update the components in our apps.
We created a dedicated CLI tool to smooth the process of creating a new component in our UI library. The CLI tool uses hygen behind the scenes to create the required file structure and templates for a clean new component.
When the new component is ready for deployment, we create a pull request in Github, with the new files. Once the PR is code reviewed, approved and merged, an automated build triggers in our CI/CD system that publishes the new code to NPM.
We use semantic-release to automate the deployment of the UI package. Semantic Release uses the commit messages to determine the type of changes in the codebase. Following formalized conventions for commit messages, semantic-release automatically determines the next semantic version number, generates a changelog and publishes the release to npm. All this is done automatically once the new component PR is merged in the repository.
Keeping the teams in sync
We have multiple product teams who work on separate end-user products that share the same UI components. The fact that we develop everything in one package helps, because there’s one place where all the components reside.
The tricky part here is: how do you make sure there are no two teams that create the same component as part of their sprint? We solve this in two manners:
- We plan ahead through a product sync (mainly on backlog review meetings) and try to catch features that are planned to be developed in separate teams, but might share similar components or might depend on one another.
- We share our sprint plans right when the sprint begins, so teams or cross function roles can alert to any overlap if it slipped through in the first review.
If there’s a team that plans to develop a new Settings page for the user, and another team plans to develop a new form component for instance, we can tie the two tasks and have the first team complete the form component before the second team starts on implementing the settings page (which will make use of the new form component).
That sensitive play is risky, as it’s not as bulletproof as it should be and things might slip through and be developed twice in two places for nothing. So we created a mother-of-all-Jira-boards (or the Vue Backlog). The huge board is a higher-level board of all foreseeable work that still needs to be done to complete the migration from AngularJS to Vue.
This means, the Vue Backlog contains tasks for every component that will be rewritten as part of the migration process. Each sprint, teams take a couple tasks from the big board into their sprints, and with that we are slowly but surely getting rid of old code. Also, such a process keeps the teams in sync but as autonomous as we want them to be. Once the ticket is in their hands, it’s unblocked and non-related to other tasks that happen within other teams.
Monitoring the process
One of the most important things in a big software project, is to be able to advance and actually reach the finish line in a timely manner. The problem with complex projects is that they are high-risk for ad-hoc changes and delays.
In our case, it is extra sensitive because as long as the migration is not done, it means we have both Angular and Vue code in the same codebase which is not fun, to say the least. The faster we complete the migration, the better success we will have in debugging our code, and the easier it will be to create new components that interop with all the rest of the components on the page.
The Jira board helps with tracking general progress, however it’s important to note that it’s not immune to changes either. It’s close to impossible to foresee all components and directives that are needed to be created until no Angular code is left.
We use multiple tools and practices to track and push our progress:
- A small script that counts the number of Angular related files in the codebase. We have the number of files as it was when the process began, we can check it once in a while and make sure we have good run rate.
- Allocating time on every team’s quarterly roadmap for infrastructure work and using that time to work on creating new components and rewriting old ones.
- Monitor the Vue Backlog burn charts, and make sure the graph is on the right direction.
- As mentioned earlier, define marathon-like sprints from time to time, in different teams, for pushing as many components as possible to the new framework.
- Infra work and FED-Ops are done independently to the front-end teams, as part of the Infrastructure team or by cross-function roles, like the Front-End Architect.
Just a point of Vue
Migrating to a new front-end framework is a good chance to make changes that wouldn’t seem sensible in a normal workflow. As the need to introduce new technologies and build new flows requires moving underlying blocks of code anyway, rewriting things better and making the infrastructure more up-to-date is a major benefit.
We’re on the first leg of the migration project and the task now is to keep it running and reach the finish line as soon as possible. Once the project is done, I’ll share lessons learned and hopefully help other companies who plan to update big chunks of core code in their systems.
Thanks for reading!
Acknowledgements: The process described in this series was a joint effort of many engineers, designers, and product managers at Fundbox. While I led the effort in my role as the Head of Front-End, the research, plans, design, and implementation are a result of the hard work of the whole team: Dima Kuzmich, Alex Volkov, Nicholas Litwin, Eyal Ella, @Amit Dadon, Itamar Gronich, Liran Hershko, Roni Litman, Idan Sagi, @Nick Below, Robin MacPherson, @Tomer Yechiel and many more.