November 27th, 2019
We at Allianz Global Digital Factory (GDF) use the tooling of Angular to keep our ecosystem in sync. Our main asset is our user interface (UI) component library, that enables teams in Allianz to use ready-made UI components for building their apps. The main techniques we use are Schematics and ng update. This is something already well established when using Angular itself (e.g., KLM story). Here we want to share our journey of utilizing such a tool stack for our Allianz ecosystem.
Imagine that the power lines in your bathroom need rewiring. A craftsman comes in, removes the tiles, fixes the wiring, seals the wall, but then runs off with the tiles lying on the bathroom floor. Not cool, right? This is pretty much what happens when software libraries ship breaking changes. They take care of improving some internal wiring but you (the application developers) are left cleaning up the parts glued on top.
For the team shipping a library, this somehow works, since they don’t feel the overall workload of cleaning up after the breaking change. For the teams using the library, it’s painful but seen as an inevitable part of a developer’s life. From a birds-eye perspective, however, this is also bad for the library team, since their ecosystem runs out of sync. Leading to different versions that have to be maintained, teams missing out on new capabilities, increasing efforts, decreasing velocity — in short, a fragile and inefficient ecosystem. Angular has the tool stack to help out here, turning updates of most applications into a one-liner. Which is what the Angular team provides for their core library:
ng update @angular/cli @angular/core
At the GDF, we wanted to leverage this for our UI component library. Our library is used within Allianz for client-facing and internal applications throughout hundreds of repositories. When moving to Angular 8 we had several breaking changes planned, for instance, changed import paths, two components were replaced by a single new component and multiple API options were removed.
This time, however, we didn’t want to burden our library users with adapting to these changes manually. We wanted to provide the same experience as the Angular team for our UI component library (named ngx-ndbx):
ng update @allianz/ngx-ndbx
Crafting the updater
How did we do that? First, we needed a mapping for every breaking change as a basis for automated migration. In our case, this part was straight forward and we could write down all breaking changes as logical mappings.
The second part was to setup the structure. We added this directly into our component library, along with the UI components. For testing we set up a local infrastructure using Verdaccio. This provides a lightweight npm server to check the process before publishing.
The last setup step was to decide how to traverse and manipulate Typescript and HTML. For TypeScript we used the TypeScript compiler API and the TypeScript AST Viewer. For HTML a common parser was sufficient. With this structure in place we started writing Schematics for all our breaking changes.
While doing this we found more edge cases than we had initially anticipated. For instance, unit tests and the programmatic use of components had to be considered. To harden our Schematics we ran tests on different real applications. After several iterations we were confident that we had covered the main use cases. At that point we could ship the major release!
Looking back we did have some bumps and small detours. One of the bigger changes was that we started with TSLint and ended up writing our own snippets for mapping code. The main reason for this was that on large projects our TSLint had long execution times and TSLint will be deprecated and replaced by ESLint. For setting up the Schematics we defined our mappings in a JSON file and wrote a number of smaller helper functions to perform them, thereby borrowing a page from Angular Material.
What definitely took some time was getting comfortable with understanding mappings in AST. Especially, since there can be different patterns that have to be considered for mapping all changes. To sum it up, especially for ng update, you should plan some time to get comfortable with the AST and to check if you have covered all relevant cases that might occur in applications.
So what happened after the release? The release is now several months back and so far we have received only positive feedback. All Allianz teams reported an easy update to version 8, for both Angular and our UI library. In most cases, the whole update was reported to have taken under one hour. We also found out that many teams were still manually updating out library. In most cases, they didn’t know about ng update and simply walked the path they were used to. Overall, we were really happy with the result.
At this stage we wanted to look closer at what was happening in our ecosystem. One of our changes was to remove two deprecated components (A & B) and to replace them with a new consolidated one. When we deprecated the old components, their numbers stayed stable. So teams were picking up the new component, but they didn’t take the time to replace the old component. This was not surprising, since teams want to focus on delivery and not spend valuable time on removing deprecated components.
With the release of version 8, we removed those components, forcing a decrease of the deprecated components. In the past, teams needed to invest time to manually replace the components. Now with ng update we are replacing the deprecated components automatically. This is a big gain for both library and application developers.
But how can this be translated for stakeholders not deeply involved in technical topics?
To help with this, we can sketch a simple case, using rough numbers that reflect our experience. If we look at our last major update, we estimate the manual update effort to be about one workday (8 hours). If we assume that 100 applications are updated, we end up at an overall effort of 800 hours. With ng update we assume each update to take one hour, which leads to a saving of 700 hours. Writing the Schematics for our UI library, on the other hand, took us about 200 hours, including setting up the Schematics infrastructure. With this simple case, we get a more than the 3-fold return on invest. At scale and from a total cost perspective automating updates is saving money and this view doesn’t even factor in the additional benefits arising from a well-synchronized ecosystem.
An important driver for GDF — besides cost reduction — was to shorten the time it takes us (the UI library team) from a request we get, to the point where the respective change is produced in a real application.
One of the main problems was that it could take quite a while for teams to adopt if they were on an older major version. Automating updates significantly reduces this time, improving our ability to ship changes faster into real applications. In addition, we can remove technical debt throughout a whole ecosystem without a big burden for the app teams. This also shifts attention from individual application silos to the ecosystem as a whole. While the cost savings of automated updating are obviously greater in a large scale environment, this technique allows any library editor to decide how and if breaking changes are handled — if you want to clean up after you broke something. This opens a new door for library developers.
Many thanks to the Allianz UI library team — currently preparing the next Schematics for upcoming version 9.