Up until last year, at LumApps we were struggling to kick start our migration of our frontend application from Angular JS to React. Fortunately, a superhero called Monorepo came to the rescue and helped things take off!
A little bit about LumApps
If you do not know about LumApps, we are a global tech company with R&D teams in France that provides our customers a SaaS Digital Workplace solution, which creates a holistic workspace, integrated with several suites and collaboration tools. Want to know a little bit more about us? Head over to LumApps.com and take a look!
Where we were up until last year
LumApps was born about 6 years ago and up until a couple of years ago, it was a SPA developed only in Angular JS by a small team of frontend developers. At the beginning of any project, usually keeping it simple allows the project to move faster and escale quickly, which is what exactly happened with our product. The project was pretty straightforward, you just needed to install the backend and the frontend and you were good to go! Development was easy and quick and time to production was as fast as you could get.
As the product grew, the company grew as well. Other frontend developers joined the team, new customers were added and new features were implemented. So naturally, more code was added and at that moment, we began to look at how our project did not scale with the amount of devs that we had, and the amount of code that we needed to create while using Angular JS. Managing controllers, templates, models and services was beginning to create a complex scaffolding that was not easy to follow and maintain, specially for the new arrivals.
In order to ease that complexity, an evaluation of the state of the art when it comes to frameworks was made and React seemed like a good alternative to start using for our new code, and we thus began our transition to React.
But in order to make this transition a little bit less violent, we decided to use a
react-element-directive that allowed using React components hooked into an Angular JS application. And this was where we were last year, with a frontend team of 15 developers, a code base that had several technologies intertwined between each other, complex to find or maintain a feature, long build and development times due to the size and lack of scability that the application had (with a backend that had the same issues as well), which ended up affecting the quality of the product and the developer experience.
We needed a change 😅
With that context, we decided to take action and drastically change the way in which we develop our components. In essence, we wanted:
- A quicker development environment without the need to launch heavy applications that are complex to configure and take a heavy toll in our computers 🚀
- Clearly separate our code, encouraging the need for documentation 📚in order to allow easier maintenance and higher code quality, and also drawing a clear line between our new code and the legacy one. We also want to increase our confidence on making a change into our code base, in order to increase our quality and clearly identify the impact of a change and what modules should be double checked.
- Encourage the React migration, in order to enhance not only the quality of our product, but also our web performance, since removing Angular JS would drastically reduce our bundle size, but developing new components in the current environment was becoming a painful task.
Monorepo comes to the rescue
After analyzing several alternatives, we decided that the Monorepo architecture would be a great fit for our use case. What we did was basically use yarn workspaces in order to create a separate folder in our repository called packages, that will hold a set of different modules separated by functionality, where each module would declare the different dependencies that it uses, define what team is the owner, and create a standard scaffolding and a set of developer tools that can be executed on each module.
For each module, developers are now able to launch a suite of tools that will avoid the huge overhead that they had while developing the legacy application:
- We installed storybook and allowed each package to create stories in order to develop their components. This removed the need for launching the backend or even the frontend application in order to code these components, and allowed the developers to do the heavy coding in an agile and fast environment. Storybook also helped us to document our project, by clearly stating the different use cases that our components have and how they should look like in that scenario.
- We added json-server in order to allow a mocked server to be executed that starts up in a matter of seconds. And with a little bit of tooling, we managed to allow each package to define a set of mocks that will be recovered by json-server on startup, and in that way, each package clearly defines the API dependencies that it has and how to mock it, so other packages can profit from it
- The monorepo approached also allowed us to better manage our unit tests and allow developers to just execute the tests associated to a specific package rather than executing an entire suite of tests or having to execute tests using paths or expressions. Just use the name of the package + yarn test and you are good to go!
- We also started coding in TypeScript, which allowed us to elevate the quality and the confidence in our product, but also using TS in a monorepo environment proved really handy since each package defines a set of types and interfaces that other packages can retrieve and reuse and having that information on a package that you do not know is quite cool.
Why did this monorepo approach worked for us?
- Our base code was still in the same repository for all of our frontend teams (legacy code included), so that allowed everyone to take a look at how other teams were working, learn from each other, but also it helped to maintain best practices and modules that are core for the application (like our Design System) in an easier way than if the code was separated in different repositories. Moreover, our legacy code was still in the same place as our new code, which became quite handy when it came to migrating modules, but also it was clearly separated from our packages that you can even only open the packages folder in your IDE and you will never see a single Angular JS line!
- Determine which packages would be impacted when a change was made on the codebase became pretty straightforward, since each package defines the set of dependencies that it needs. Upgrading a dependency across multiple packages is now a simple task, and we can now see how our modules depend and interact between each other from each package's package.json
- The new scaffloding and package oriented development gave us a new perspective when it came to coding components and how they will be used from other ones. Figuring out what to export, how to export it, defining clear and simple props that would be used by another dev became a great discussion to have will reviewing a PR. This gave us a new quality standard, one which the Developer Experience mattered, and one where the way you coded a package could have an impact on another one. It gaves us back that sense of being the same frontend while having clear boundaries and scopes for each team. And defining code owners for each package is great as well when you have a growing team!
What did our devs say?
So we did a survey after a couple of months working with the new architecture in order to determine if we had taken the right decision of using a monorepo and as well as see if our frontend devs were happy with this change.
So when it came to the Legacy application, our devs thought that:
When it comes to the New architecture with a Monorepo, our devs think that:
What are our next steps?
Now that we have a path to React that scales for our current scenario, we are still have a lot of ground to cover when it comes to the migration, since we have many lines in Angular JS.
Our next goal is to migrate an entire page in React JS and start separating our legacy application from the new one. The idea will be to have entire pages on full React, meaning that if a user clicks on a certain link and that link shows a page entirely migrated, we will load a new page that only has React code. We will definitely profit from yarn workspaces here in order to create new packages for our frontend applications. More on that on a next post!