Rethinking Component Libraries
Part Two | Understanding Costs
This is the second of three articles discussing how to think about component libraries. In Part One, we talked about redefining the value of shared component libraries. Now we’ll talk about understanding costs — another often misunderstood aspect of component libraries. This is not meant to be an exhaustive list, but rather common forces that shape shared libraries. Specifically we’ll discuss:
- Library size
- Component complexity
- Adoption rates
- Organization volatility
Understanding how these costs behave allows me to look beyond the code and discern what makes a shared component library effective as well as common points of failure. I hope reading this will be helpful for you as well.
I’d like to start by saying that building an in-house component library is not cheap. It is very expensive and taxing to do well, and it is even more costly when executed poorly. It is not the budget option. If you need a lower-cost solution, you’re much better off choosing one of the many great external libraries available. Should you instead choose to build a custom shared component library, do it because your organization sees the potential value as worth the significant investment needed to make it work. We’ll return to this concept again, so it’s worth saying here:
The purpose of understanding costs is not to find cheap solutions. Instead we’re optimizing for a point where we are able to maximize the library’s value while also keep costs sustainable.
Size is often the largest cost contributor for a component library. Many maintainers find themselves in a position where they’ve added too many components, and the cost of maintaining the library becomes unsustainable. We’ll talk more about how to avoid that fate when we discuss optimizations in Part Three. There are a few things to note before we dive into the details of this relationship:
- Each component in your library has its own intrinsic cost
- A component’s cost is not necessarily fixed and can often be adjusted
- The library’s total cost is more than the sum of the components’ cost and is relative to your team’s capacity to absorb it
The early growth stage is most common time for teams to balloon their library size. There is typically a high degree of volatility: API patterns are developing, the governance model is in its infancy, and there’s strong pressure to keep up with consumer requests. All of these factors can cause maintainers to overshoot the library’s critical mass and run too hot for too long. This inevitably results in large breaking changes and a lot of volatility for consumers. But the volatility’s impact is diminished in this stage because of lower adoption rates, which we’ll discuss later. If maintainers are able to find their feet in time, the end of the early growth stage is the best time to make and learn from those mistakes.
There is no perfect size for a component library. But you’ll find that as you add more components, each new component will have a diminishing return on value over cost, especially as the components become more complex. Again, the goal is to optimize for value while managing costs.
Component complexity is another large cost driver for libraries. Complexity isn’t necessarily bad, but it does need to be properly managed. Some components are solving complex problems: Focus management, list systems, and stacking order immediately come to mind. And components need to absorb that complexity to provide a high-level of value to consumers. You can usually reduce a component’s complexity, but only up to a certain point before you start to significantly deteriorate value. And it’s important to recognize that effective systems will still require a significant cost to manage those complex problems.
The cost of complexity can also grow as components are modified to support new use cases. The cost of new functionality is frequently distorted by only accounting for the effort to add it the library. But as a component’s complexity increases, it becomes more resistant to change. And this resistance compounds the cost of maintenance over time. So while the cost to add new features was relatively cheap, the total cost to the library can be quite large.
As your library becomes more established and grows in value, your adoption rates should go up. We can think of adoption as a combination of the total number of consumers and their saturation of usage. That rate is a common metric for shared libraries, and it’s really exciting to see it go up. But it also comes with associated costs. As consumers use your library more heavily, they’ll be increasingly coupled to your team. And each new team will have new requirements, evolve differently, and inevitably file more issues. This cost is deceptive because its growth is not linear. The mechanisms that worked well at one level will be entirely ineffective at the next. You’ll constantly need to recalibrate and retool to keep pace.
The other challenge with this cost is that it can drive the previous two costs: library size and component complexity. There will always be a gap between what consumers want in the shared library and what you’re able to sustainably support. This creates a constant pressure to add more complexity to existing components or add new components as adoption grows. Sometimes the cost is justified, but you’ll always need to weigh it against the value being added.
We can think of organizational volatility as a company’s or organization’s rate of change over time and how that change impacts teams. A very high rate of volatility is usually described as “thrash” and leaves a lot of confusion and frustration in its wake. But any growing organization will experience some level of volatility. The company can shift priorities, reorganize teams and ownership, or decide to merge or be acquired with little notice. All these changes can generate costs for teams and your library. Managing the cost of volatility is always challenging because there is little that can be done to control it directly.
But while it is an uncertain and unwieldy cost, it is also a huge opportunity for creating value. As stated previously, helping teams become more resilient and adaptive to change is the key value of a shared code library. But it goes beyond the code, which is something we’ll talk more about in the next article.
Thanks for Reading!
Understanding how costs behave for shared component libraries has been incredibly valuable for me, and I hope this was useful for you as well. Part Three will focus on how to manage these costs and optimize for adaptability to deliver a high-value library. I’m writing these posts as I have time and headspace, so I’m not sure when I’ll finish the last post. But if you have questions or just want to say thank you, you can find me on Twitter.