Refactoring an Angular Legacy Codebase
Since I have arrived at a new team at DB InfraGO, I am about to dig into the codebase of the main project. It has grown over the years and has around 300 Angular components. As usual the project was developed some years ago and uses the standard practices of its creation time. However, Angular has moved on and therefore requires regular maintenance and refactoring.
The main problem with the project is it’s long loading time. It loads a massive main.js and fetches loads of data initially from the backend. Most of that data is not needed on the requested page, but is fetched in some constructor logic. This is eager loading at its purest.
Lazy Loading and Standalone
So the first refactoring goal is to migrate to lazy-loaded routes. This means that only the code chunks needed for the current route are loaded. The app is separated in several chunks which are requested on demand. But for lazy-loading to work, all components must be standalone, which is the standard behavior since v19.
Standalone components are a good example of how Angular gradually introduces new features and approaches. At v14, it was introduced and optional. You could use it, try it out, but there was no need to switch. In v19, standalone became the new standard. The Angular version switch included a task to note all legacy components as standalone: false. And this is what I encountered in the codebase, no component was standalone.
For the project owner it was important that every technical improvement was gradual and should not disrupt the system availability. So I went for a step-by-step approach, refactoring part by part. First, I identified shared pipes, directives and components, which do not have internal dependencies. Those can be refactored, added to the module imports/exports instead of declarations and the app works as before.
I also tried AI-assisted refactoring. But the agent found after some components a short cut I was not even aware of. It imported an AngularMaterialModule, which sounded okay. But as it turned out, this was an internal module bundling all dependencies of Angular Material together. So not exactly what I expected or needed. Because every component should only import those parts of Angular Material it needs to reduce the initial bundle size.
MVVM
Once lazy loading is finished, I can target other issues, namely the Model-View-ViewModel approach. This comes from the Microsoft WPF world, where it is a widely used architecture pattern for applications. But strictly converting it to Angular with it’s component-based architecture has not always worked well.
Conclusion
So I am looking forward to refactor this huge codebase to make it maintainable and extendable. I remember only few refactoring projects of this magnitude.
- Upgrading Sitecore with a two-major-version jump at comparis.ch
- Introducing a JavaScript module system at comparis.ch
- Doing a major version shift with Ionic Framework at wer denkt was