Building a system of systems: What we didn’t know in 2019
Six years after introducing the system of systems model, what it became in practice, where the original vision held, and where it gave way.

I spent nine years at Spotify leading design systems and accessibility. During that time, I learned as much from what failed as from what succeeded. I’m sharing these insights not to critique decisions made at the time, but because I believe they’re valuable and can help others navigate similar challenges. This article is part of a series exploring those lessons.
In November 2019, I stood on stage at Design Systems London and introduced Encore, Spotify’s new design system, and the approach we were taking to build it. The recording has been watched nearly 35,000 times. The article we published alongside it became the most-read post on the Spotify Design blog before the blog was shut down. People still ask me questions about it today.
Our approach was a system of systems. Instead of the typical monolithic design system owned by a single central team, we proposed a family of design systems, owned by different teams across the organisation, all operating under a shared framework. It was a direct response to the landscape we were building for, and the cultural realities of how the company worked at the time.
Six years on, I want to tell you what actually happened.
The model did its job. By any measure, the system we built is the most successful design system the company has ever had. It gained traction quickly, outlasted every previous attempt, replaced 22 competing systems, and became established as Spotify’s design system. But a thing can succeed and still be full of decisions you’d do differently, and there’s a lot I wish I’d known in 2019.
This is the first of three articles sharing my experiences building a system of systems. Here I want to cover what the model actually became in practice, where the original vision held, and where it gave way.
What a system of systems is
A quick definition first, because there’s still surprisingly little written about this in a design systems context. Nathan Curtis was exploring this territory around the same time, most notably in Design System Tiers and Consolidating Design Systems, and has since published a collection of writing on the subject. If you want the theory, start there.
In simple terms: multiple design systems coexisting within a shared framework. Different teams own different systems. Each system has its own scope and audience, and each builds on a shared foundation.
Our implementation had three layers:
- Foundation was the core of everything. Tokens, colour, typography, spacing: the shared building blocks every product needed regardless of platform. Thin by design, and it went everywhere.
- Core systems sat on top. Platform-specific systems for web and native mobile, owned centrally by the design system team, built with the rigour you’d expect from a traditional design system.
- Local systems were the outermost layer. A local system serves a specific team, context, or domain. It builds on the core systems and extends them with components and patterns too specific to belong anywhere else. One for consumer mobile, another for creator tools, and so on.
A shared framework tied it together. Tooling, infrastructure, a single documentation website, shared rituals, and a programme of work to keep everything aligned.
Inheritance is what held it all together. Each layer inherited from the layer below it. A local system carried the components from the core systems, and those components carried the tokens and guidelines from the foundation. Pick a local system, and you get everything underneath it, whether you know it or not.

This made the pitch to our customers simple. You only need to know about one system. Find the one closest to your use case, use it, and we’ll take care of everything underneath.
A good pitch; it turns out achieving it would be a lot harder than we anticipated.
The org chart got into the architecture
You can’t talk about building systems in tech without dragging Conway’s Law into it at some point, so let’s get it over with. Conway’s Law is the observation that organisations create systems which mirror their own communication structures. We all know this; we all nod along and agree this is bad, yet it always finds its way in.
Our system mirrored the org chart beautifully.
The local systems mapped directly onto it. Each one was owned by the department or product area it served, and scoped to it. Components built for one product belonged to that product. Teams in adjacent areas had no clean route to reach them.
Technically, this was by design. We wanted the people closest to the problem to own local systems. We didn’t plan for the walls that came with this.
The same pattern showed up inside the core systems. The foundation and the web component library were separate packages and separate Figma libraries, owned by separate teams. From an organisational ownership perspective, that made sense. From a customer’s perspective, it meant installing two packages and connecting to two Figma libraries just to build a web page. The split existed because of how we were organised, and for no other reason.
The internal architecture can reflect team ownership. The experience of using the system should hide it. We didn’t get that quite right; it’s one of the first things I’d fix.
The brand we gave away
Building a strong internal brand for the design system was deliberate. Before Encore, we had 22 competing systems, each with its own name and identity. Getting people to rally around a single name took real effort, and once it was established, the name carried weight.
Local systems were part of our brand-building strategy. Each one carried the core system’s name. Our goal was to reinforce the idea of family, make everything feel like part of the system, and reduce the cognitive load on customers who just wanted to know which system to use.
It worked. One name became the thing people associated with design systems across the company. It also created a problem we hadn’t thought about.
When you hand your brand to another team, you hand over control of what it means.
One of our local systems took a non-traditional approach, which I covered in detail in the federated design systems article. It served the company’s biggest and most rapidly iterated platform, and its architecture made reuse hard. Something as simple as reusing a component from one view to the next wasn’t easy. Asking that team to adopt the design system as it stood in 2019 would have turned an app architecture problem into a design system problem, and that was never ours to solve.
So rather than providing reusable components, they encouraged developers to duplicate code under a specific naming convention. The intent was traceability and speed, backed by a real belief in the team that more reuse didn’t automatically mean better engineering outcomes. The challenge was that customers arrived expecting what a design system normally gives them: configurable components that match the designs they were handed, and found the opposite.
The same question appeared in the support channel almost daily. “I’ve got this component in my design, how do I implement it?” The answer was always the same: “You build it yourself”. And when people talked about that frustration, they talked about the design system.
We could see it in the data. Our quarterly satisfaction surveys across design and engineering showed most of Encore sat somewhere in the 70 to 80% range. The problematic local system hovered around 30 to 40%, survey after survey. It was consistently, measurably the worst experience anyone had with something carrying our name.

The nuance between the core system and the local system was lost, exactly as we’d intended when we designed the naming strategy.
We’d made everything feel like one thing, so when one part of it failed, the whole brand took the hit.
It got bad enough that leadership asked: should we rebrand entirely? Start again with a new name and put some distance between ourselves and the damage. The core team had done nothing to cause it, but that was beside the point. We ultimately decided against rebranding, figuring repairing the damage was less work than starting over.
Looking back, we should have been clear from the very start that what this specific local system team built was a naming convention for a hard architectural problem. A sensible one for its context, but it was never designed to meet the expectations our name set for it. When I spoke to Encore’s former Design System Lead about this, he summed it up:
A perfectly designed system cannot resolve imperfectly designed architecture.
Your brand is an asset, and you’re accountable for it. If other teams want to use your name, you need real governance over what that name gets attached to. Handing it out freely might buy you a sense of family today but risks becoming a liability tomorrow.
So, a question for your own system: if another team put your design system’s name on something you had no control over, what could you actually do about it?
The promise of a single system
Our model’s central pitch was that customers should only ever have to think about one thing. All the complexity underneath, the different systems and teams and layers, was ours to manage.
We put real effort into delivering on it. We redesigned the documentation website several times to reduce friction. We removed an upfront landing page where customers had to pick a system before they could see any content, moving the system picker inline to get people into content faster. Eventually we flattened the whole site to expose less of the structure underneath.
We never truly got there. At some point, customers had to understand the internal architecture to navigate the system correctly. Which system do I use for this project? Is this component on mobile as well as web? Why are these two things in different packages? The questions kept coming, because the system kept reflecting our organisational complexity at the people trying to use it.
When it worked too well
There’s an irony buried in our failure to truly resolve the promise of a single system. In the places where we did succeed in hiding the complexity, where customers genuinely experienced everything as a single system, we created a different problem.
When everything feels like one system, component ownership becomes invisible. Customers browse the documentation, find a component that solves their problem, and use it, without noticing or caring which system it came from.

The most extreme example of this was a local system’s player component. It was built for a high-density, web-based advertising management tool, and it ended up inside the native consumer mobile app. A web component, designed and maintained for one very specific context, was embedded in the main Spotify app, sitting alongside the native player, creating two distinct player experiences inside the same product.
From the customer’s point of view, this made complete sense. They were moving quickly, found something that worked, and they used it. From the local team’s point of view, they’d built something for a small, known audience and suddenly found themselves supporting a much larger one they’d never planned for.
The consequences go beyond support load. A local system is deliberately built with less rigour than the core, because the assumption is that its components will only ever be used in contexts the local team controls. Once those components escape that boundary, that situation changes. Does the team now need a more careful release process? Deeper accessibility and localisation reviews? All the overhead the local system model was designed to avoid.
The better we got at making everything feel like one system, the more we eroded the boundaries that made local systems viable in the first place.
What stayed true
For all of that, our original goal held.
The model achieved what nothing before it had managed at the company. It created a stable, shared foundation across dozens of platforms, thousands of engineers, and hundreds of designers. It stopped the proliferation of competing systems. It gave the organisation a real source of truth, and it’s still there.

The local systems, for all the problems in this first article, gave teams genuine autonomy without forcing them to choose between moving fast and using the design system. Every previous attempt had forced a choice, and every previous attempt had failed.
A system of systems might be the right approach for organisations operating at this scale. The specific shape we gave it in 2019 needed to change and evolve, and understanding why means understanding what we underestimated along the way.
That’s what part two is about.
Read part two: What we didn’t see coming.