Building a system of systems: What we didn’t see coming
The costs we never planned for: the hidden load on the core team, local systems that stalled, a programme that came apart, and success the model had nowhere to put.

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.
Part one of this series covered the gap between what we pitched in 2019 and what the model became: an org chart embedded in the architecture, the brand we gave away, and a single-system promise that both failed and succeeded in equally awkward ways.
This article is about what caught us out.
A common thread runs through all the stories here: people. We’d built the model on assumptions about how they’d behave. Local system teams would maintain their systems. They’d contribute components. They’d stay aligned with the programme. They’d protect the brand. Sitting in a room, in Stockholm, in 2019 with all the founding teams, all of that felt reasonable. We even found time to recreate a Renaissance painting.

In practice, teams had other priorities, real priorities, and the model had no answer for what happened when they won.
The hidden load on the core team
When we designed the model, we were focused on what it would enable. Flexibility for local teams. Autonomy. A design system that worked with the grain of the company rather than against it. We spent little time thinking about what running a system of systems would cost the core team.
This showed up in three ways.
Support was the most immediate. Our positioning was that everything was one system, so customers shouldn’t have to think about which team owned which part. They used the system and asked for help when they got stuck, so when someone had a question about a local system, they asked us. We were the public face of the design system. We took the request, figured out which local team owned it, and brokered the handoff. A handful of requests a week sounds like nothing. The interruption cost adds up fast. And local teams, who were running their design systems as a part-time responsibility alongside everything else, couldn’t always respond quickly. We often found ourselves stuck between a customer who needed help and a team that didn’t have time for them.
Maintenance of the shared infrastructure was quieter but more persistent. Putting all the web systems in a single monorepo was a deliberate decision. Local teams got shared infrastructure for free. They could stand up a system without building their own tooling, pipelines, or release processes. The core team owned it all, and local systems inherited the benefit. We hadn’t fully thought through the consequences. The core team also owned the cost. Initially, it wasn’t too bad, but over time the scales tipped, and the team was spending more and more time maintaining the shared infrastructure. When something broke or needed updating, it came to us. If a change touched a local system and that team was busy, we absorbed it. Again, not dramatic on any given day. Cumulatively, it was enough to notice.
Clean-up was the most expensive part, and we never anticipated it. Over time, a number of local systems were quietly abandoned. The teams that created them moved on, interest faded, staffing changed. The systems didn’t disappear. They just stopped being maintained. And because they carried our brand, they became our problem.
Leaving abandoned systems attached to the design system brand wasn’t an option. The reputational risk was too high. So more than once, we carved out roadmap time to decommission these abandoned systems, migrate remaining active users, tear down the infrastructure, and remove the association. Each decommissioning took anywhere from a few weeks to a month of engineering time. Work we hadn’t planned, on an already full roadmap.

Add it all up, and the core team was routinely pulled away from the work we were actually there to do. Every hour spent on unplanned support, maintenance, and clean-up was an hour we weren’t spending moving the system forward and solving real business problems.
Whatever you think a distributed model will cost your core team, it will cost more. Budget for it, or build something simpler.
Local priorities always win
Our model assumed local teams would stay committed to their systems over time. That turned out to be optimistic.
The pattern was consistent, and it repeated across multiple systems. Someone created a local system because they believed in it. A design lead, an engineering manager, a particularly engaged individual. They made the case, secured buy-in, and got the work started. While that person was there and engaged, the system moved.
When they left, or shifted focus, or got pulled onto higher-priority work, it stalled. Slowly, rituals became less regular. Documentation stopped being updated. Components stopped being added. Eventually the system was being used but not maintained. Then it was barely being used either.
If you’ve worked in a large organisation, you might recognise this. Work that depends on individual advocacy is fragile. The moment the advocate stops driving it, it’s at risk. What made it acute here is that local system ownership was structural. These weren’t informal side projects. They were listed on the documentation website, carried our brand, and were presented to the whole company as functional and supported. When maintenance stopped, the gap between what they appeared to be and what they actually were became a problem for the customers relying on them.
I wrote about one example in detail in the federated design systems article. In that case, the drift went deeper than shifting priorities. The local team’s leadership never understood the value of the design system work, so they pulled the team onto other projects. The system work never got the focus it needed because the people setting priorities didn’t believe in it, or understand it enough to protect it.
This is what I refer to as the local priority problem. Local systems lived inside product organisations with their own goals, roadmaps, and leadership. Those leaders hadn’t been part of the conversations that established the model. Local system teams were accountable to their own departments first. When local system work competed with product work for time and attention, product work won. It will always win.

Was that really a problem with the model, or a problem with our version of it? We put enormous effort into building a cohesive family: shared branding, shared documentation, a programme designed to keep everyone rowing in the same direction. That ecosystem depended on every local system staying engaged. The moment one drifted, the consequences were visible. Brand damage. Support gaps. Abandoned systems. If we’d built something looser from the start, with less shared identity and fewer expectations about alignment, would a local system quietly running out of steam have mattered as much?
Probably not. Which suggests the local priority problem had less to do with the nature of local systems and rather more to do with how tightly we’d bound them to the core.
What happened to the programme
We knew from the start that a design system spread across multiple teams in different parts of an organisation would need dedicated coordination. So we built a programme layer above the individual systems: shared governance, shared rituals, shared planning. And we hired a brilliant programme manager to run it.
It worked. For a while, it worked really well. The system of systems was made up of teams from four separate parts of the organisation, but we felt like one entity. We had visibility into each other’s work, processes for surfacing conflicts early, mechanisms to enable greater alignment, and the kind of cross-team relationships that make collaboration feel natural rather than effortful.
Then three things happened in quick succession.
COVID moved everyone to fully remote working overnight. A wave of leadership changes brought new people into the local system teams, people who hadn’t been part of the founding conversations and who took their direction from their own managers. And our amazing programme manager, the connective tissue holding it all together, received a well-deserved promotion into a different role.
We could have absorbed any one of those. Together, they pulled the programme apart.

The remote transition hurt us in a way that was easy to underestimate. We’d built relationships with local system leaders in person, over years. Conversations that started about something else and ended up being about the design system. Small moments of alignment around the coffee machine helped keep everyone pointing in the same direction. None of that survived the move to a screen automatically. With the original group of local leaders, there was enough shared history to hold things together. But that group had mostly moved on, and we were working with new leaders with no relationship or history to fall back on.
When our programme manager moved on, running the programme fell to the design system leadership team. People who already had full-time jobs running teams, managing their reports, and driving their own roadmaps. The programme didn’t get the attention it needed, and we knew it. Rituals gradually fell away. Cross-team planning stopped. Communication between the core and local teams became patchy and, in some cases, stopped entirely.
We repeatedly argued for backfilling the role. Leadership decided not to backfill. The gap lasted nearly two years. I believe this was the single most consequential decision of the whole period. The impact was invisible to the people making the call, and by the time it became obvious, the damage was done.
Not backfilling the role was the wrong call, but we’d also made it far too easy for one departure to do this much damage. So much of the programme lived with one person: the relationships, the rituals, the knowledge of who needed to talk to whom. We’d never built it to survive them leaving. That was our failing.
At the scale we were operating, programme management in a system of systems is what makes the model viable. Take it away, and you’re left with a collection of separate systems that happen to share a name. The coordination overhead doesn’t disappear when you stop funding it. Everyone else absorbs it, less efficiently and at compounding cost.
The coordination overhead doesn’t disappear when you stop funding it.
When a local system succeeds
Not every local system story ended in abandonment. The most successful local system in the programme’s history was still going strong when I left. It served the advertising platform. Smith Schwartz and Tyce Clee, who led design and engineering, discussed the work in detail on the On Theme podcast. It’s worth a listen. Here’s what set it apart, and what it delivered:
- Strong design leadership, with buy-in that went all the way to the top of the business unit
- An engineering manager who had come from the core design system team and understood the model deeply
- A full product redesign delivered over the course of a year
- A 20% reduction in the time it took to complete a campaign booking, measured with support from data science
- Dark mode shipped quickly, on the back of proper investment in tokens
This is what a local system was supposed to be. Close to the problem, moving fast, and delivering outcomes the core team could never reach on its own. When people ask me whether the model worked, I reach for this example.
It also exposed a major gap in the model.
A team delivering at that level builds credibility and confidence, leading to bigger questions. Their products had needs the core system served poorly: denser layouts, tighter spacing, patterns and components that business tools needed. So they built for those needs, and what they ended up with was a complete package, a system genuinely well suited to business experiences.

People who work on design systems instinctively want to make good work reusable. That instinct is where Encore began. So, after building something that worked, the team started thinking beyond their own products. An idea began to form, informally and at a high level: perhaps the core system and core team should focus on consumer experiences, while this ads local system team focused on business experiences.
The trouble was our model gave that instinct and their success nowhere to go. Some of the individual pieces could travel, but the whole, proven, system-level way of doing business experiences had no path upward. So the only way the team could see to spread their success was to widen the boundary around it.
Our model gave that instinct and their success nowhere to go.
We had no answer to this, because our model had never contemplated the question. It described how local systems were created, what they could build, and how they related to the core. It said nothing about what should happen when a local system got so good at serving its corner of the business that owning that corner outright started to look reasonable.
Looking back, a team with ambition wasn’t the problem. We designed our governance structures around the things we feared: fragmentation, drift, systems going quiet. We spent almost no time designing for the outcome we should have wanted most: a local system doing so well that it starts asking for more.
The promotion pipeline that never was
Which brings us to the idea we were most excited about in 2019, and the one that let us down hardest.
Component promotion was the mechanism that made the whole model make sense. Local systems sit close to the problem space. They move fast. They build things the core system hasn’t got to yet. When a local team builds something with wider value, it gets promoted into the core, so everyone can benefit. Local systems become the design system’s research and development arm. Good ideas travel up and into the core system.
In six years, this happened fewer than a handful of times.
The reason was pretty straightforward. A local system component solves a specific problem for a specific audience. The core system serves everyone. To make that jump, a component has to be generalised, documented to core standards, aligned across design and code, reviewed for accessibility and localisation, and shipped through a release process that touches every team using the system. That’s weeks of work, and it competes with everything else on our roadmap.
Component promotion lost almost every time. It was easy to agree with and hard to justify what it cost.
So useful components stayed where they were, got used outside the audience they were built for, and created the dependency problems I described in part one. The pipeline we’d imagined was an aspiration dressed up as a process.
Component promotion lost almost every time. It was easy to agree with and hard to justify what it cost.
This is the thing I’d most want to fix, and it’s where part three of this series starts.
None of this was anyone’s fault
It’s worth specifically stating that every team in this article was doing their job well.
The local teams that let their systems drift had product roadmaps to deliver. The leaders who deprioritised design system work were accountable to their own organisations. The team whose ambitions outgrew their remit had earned the right to think bigger. The people who chose not to backfill a programme manager were making a resourcing decision with the information they had. And we’d built a programme that depended on one person without realising it.
That’s the lesson I’d give to anyone building a system of systems today. Design your system around how people behave inside your organisation, with the incentives they actually have. Not around how you’d like them to behave once they’ve read your documentation.
Part three is about what I’d change and do differently today.