Tobias Grundmann is a product manager at Digitec Galaxus responsible for the core application and framework, as such KPIs like LCP & friends have become his daily bread.
Creating ownership for web performance metrics is challenging, especially if you want to ensure your entire organization is pushing along. Alex Russell has an excellent maturity model that captures this enduring journey (see infrequently.org blog). After navigating much of this journey over the past few years at Galaxus (www.galaxus.ch), I’d like to share one simple — yet, in my opinion, genius — approach that helped us generate visibility and create shared ownership for web performance.
If you’ve ever used the Web Vitals Extension or the Web Vitals JavaScript library to monitor your web vitals, you’ve likely come across the element
field in your console and observability logs. Since you’re reading this post in the Web Performance Calendar, I’ll also assume you’re using CSS minification. If so, you’ve probably been greeted with cryptic messages like this:
{ name: "INP", rating: "needs-improvement", value: 400, attribution: { element: "div.sc-e14acb34-0.l3D4m>img", ... } ... }
Is the element field like this useful when you collect those logs on a RUM? Not in this state!
However, once you know that those minified class names are fairly static (we do many deploys per day) and those names are included in your build artifacts (aka source maps), you unlock a treasure trove of actionable insights.
Let me explain how:
The element
field indicates the HTML element responsible for the web vitals event and includes the minified style classes. In this case, the class is sc-e14acb34-0
. If you walk up the html tree through the parents you get an ordered list of CSS classes for the HTML tree which could look like this sc-d60e1713-0 > sc-87305eea-0 > sc-ff87a422-0 > sc-e14acb34-0
. A source map (see web.dev’s article on source maps), which can be generated during your build process, resolves these minified keys back into their original class names and even the file name they originated from. All you need to ensure is that each file in your codebase has an assigned owner. In our case, each package in our frontend monorepo is assigned an owner via a property in package.json
. With every build, we collect the meta data for each static css-class and upload them to a Redis cache for easy lookup.
As a result, every web vitals event we capture is automatically enriched with additional metadata — such as the corresponding web component, code file, and the owning team — via a cache lookup before being stored in our RUM solution. The enriched data looks something like this:
{ type: "INP", rating: "needs-improvement", value: 400, _cssClasses: "sc-d60e1713-0 > sc-87305eea-0 > sc-ff87a422-0 > sc-e14acb34-0", tree: "DesktopLayout > Page > ContentStyled > StyledProductTile", component: "StyledProductTile", file: "libraries/product-tile/ProductTileStyles.tsx:38", owner: "Team ABC", ... }
With this enriched output, we now have dashboards that clearly identify which components are causing performance regressions. Allowing us two major things:
- Experiment with component changes and measure performance improvements in production, celebrating improvements one step at a time.
- Automatically generate technical debt tickets for the responsible teams, which again ensure continuous improvement.
For us, this added visibility has been a great tool in creating a culture of responsibility and shared ownership for web performance. Perhaps it can help advance your web performance maturity as well.