Originally published at https://blog.joonquixote.com/en/posts/the-story-of-my-failed-design-system/.
There are plenty of articles about design systems.
Many organizations have shared stories about adopting them in practice, and there is no shortage of conference talks and articles about designing tokens.
What is harder to find are stories about design systems that were built but never made it any further.
I looked for examples like that while working on one myself, but found very few.
So I decided to leave a record of my own experience.
From the spring of 2022 to the fall of 2024, I spent about two and a half years building a design system.
We released the component library, but the broader system we were building on top of it was never adopted by a product.
The organization responsible for the project was eventually disbanded.
Along the way, I even joined a video call with a Figma engineer at 11 p.m. Korea time.
Now that I am no longer part of that organization, I can write about the experience a little more openly.
This article is based on memory, so some details may be incomplete or inaccurate.
It is also a personal retrospective, not an official project report.
I have left out details that would only make sense inside the company and focused on experiences that may still be useful to readers outside it.
Why We Started Building a Design System
It was the spring of 2022.
At the time, there was no single frontend stack used across the company.
Some teams used React, while others used Vue or Angular.
Because the stacks differed, teams repeatedly built their own versions of components with the same design.
Even teams within the same division were doing this.
The design system project began as an attempt to reduce that duplication and allow multiple applications to share the same components.
Starting at the company-wide level would have made the scope too large, so our initial goal was to build a system that teams within our division could actually use.
The biggest technical decision was to use Web Components.
Implementing the library in React would probably have given us the fastest initial development speed.
However, a library tied to a particular framework would have made it difficult to support teams using Vue or Angular.
Sharing the same implementation across multiple applications required a framework-agnostic foundation, so we chose to build Web Components with Lit.
This allowed each application to use the same component interface regardless of its framework.
I was responsible for components such as tabs, pagination, and tree.
Rather than wrapping an existing library, I designed and implemented them from the ground up.
During development, I kept the source code of Ant Design and Spectrum open side by side and studied why each implementation had been structured the way it was.
The library was eventually released within our division and adopted by another team that was preparing to launch its product.
Their feedback informed further development.
By the following spring, we had reached the v2.0 release candidate.
One particular error from that period has stayed with me.
If the same Web Component is registered by more than one package, the browser throws an error in the Custom Element Registry.
Shared components such as icons caused this problem because they were used by multiple packages.
We resolved it by declaring the shared package as a peerDependency and restructuring the dependencies so that the application installed it only once.
That error gave me a much clearer understanding of which dependencies in a monorepo must exist as a single instance.
While working on v2.0, I also learned that implementing components alone is not enough to sustain a design system.
We also needed a development environment that ensured new components followed the same standards and were released through the same process.
Writing rules in documentation does not tell you whether anyone is following them.
When someone breaks a rule, the system needs to detect it automatically.
I Wanted Figma to Generate Code
In early 2024, I moved to a task force.
My role there was to extend the design system.
Even with a component library in place, developers still had to manually align their code with screens designed in Figma.
We wondered whether we could reduce that gap by reading the structure and properties of a Figma design, converting them into an intermediate representation, and then transforming that representation into working code.
That idea became the starting point for the extended design system.
The use of an intermediate representation was similar to the structure of a compiler.
A compiler does not translate source code directly into machine code.
It first passes through an AST, or Abstract Syntax Tree.
In the same way, we converted Figma’s design data into a normalized structure before generating code from it.
This meant that adding a different output target would not require rebuilding the entire transformation process.
Early in the project, we called this intermediate representation DST, or Design Syntax Tree.
We later developed it into an XML-based markup language that described Figma’s design structure using elements such as <Frame> and <Layout>.
At that point, we began calling it DSML, or Design System Markup Language.
DSML served as a shared language for consistently representing and exchanging Figma design structures within the team.
Initially, I built the part that transformed Figma API responses into the internal tree structure we then called DST and rendered it as HTML.
From that summer onward, I worked on the code generator by myself.
Once the representation had been formalized as DSML, the generator took DSML as input and produced JSX.
It was clear that JSX would not be our only output target.
I therefore kept the transformation logic in the core and separated the inputs and outputs into adapters, following a hexagonal architecture.
By September, we had completed the demonstration and project report.
The Battle with Figma, and an 11 p.m. Call
At the same time, the task force was also developing a plugin that allowed designers to enter configuration values and generate the components required for a design system in bulk within a Figma file.
When the number of generated components was small, everything worked as expected.
But when we tested the plugin at the scale of an actual design system, memory usage exceeded 4 GB and Figma became unresponsive.
Figma displayed a warning that the plugin had frozen.
We followed every procedure described in Figma’s documentation for frozen plugins, but the result was the same.
I contacted Figma Support.
I asked whether the API provided a callback that would allow us to release memory after local processing had finished but before the changes were synchronized to the cloud.
Since Figma runs on Electron, I also asked whether there was any way for a plugin to trigger garbage collection manually.
The support team suggested periodically yielding through asynchronous timeouts to give garbage collection a chance to run.
We had already tried that.
Eventually, I asked if we could speak directly with a Figma engineer.
Because the engineer was based in the United States, we scheduled the Zoom call for 11 p.m. Korea time.
I shared the link with the team and asked whether anyone else wanted to join.
I was not comfortable enough with English to explain everything I had prepared.
Even so, I was able to confirm the conclusion.
The issue did not appear to be a bug in our plugin.
Figma and its plugins are each allocated memory, and creating that many nodes in a single operation was itself reaching Figma’s memory limit.
The final response we later received from the support team said the same thing.
Our only option was to split the work across multiple files and reduce the number of components and instances in each file.
We did not solve the problem.
But we did establish exactly what we had run into.
It was not a problem with our code.
It was a limit imposed by the platform, and no amount of code-level optimization would allow us to exceed it.
Hearing that directly from an engineer, after receiving the same answer by email, left little reason to keep hoping for another solution.
Then the Organization Disappeared
Even after the project report in September, no concrete direction was established for the next stage.
In October, the company went through an organizational restructuring as our organization was spun off from the parent company into a subsidiary.
Internal development groups without a clear business case were being wound down, and the task force was disbanded as part of that process.
The product manager and designers who had participated alongside their primary roles returned to their original teams.
The developers were reassigned to different organizations.
The code generator was never adopted by a product.
The memory problem in the Figma plugin remained unresolved.
The extended design system survived only in its repositories and project reports.
Looking Back
Looking back, some conditions were clearly beyond my control.
The first was the limitation of the platform.
If we wanted to generate designs at scale on top of Figma, we had to work within the boundaries Figma allowed.
Those boundaries had little to do with how well the plugin itself was written.
The organization’s priorities were also beyond my control.
The spin-off and restructuring would have happened regardless of the task force’s output.
At the time, the organization was not being evaluated based on how complete its work was.
In the end, whether both the organization and the design system would survive was determined by two conditions that code alone could not address.
Still, I do not think everything can be attributed to external circumstances.
There were also things I missed.
The first was that I failed to work on adoption alongside development.
I focused on implementation.
I could explain why the hexagonal architecture was necessary and demonstrate how the code generator worked.
But I did not present the result in a way that allowed decision-makers to understand whose problem it solved, what that problem was, and why the system was needed at that particular time.
The demonstration explained what we had built, but it did not lead to actual adoption.
I also regret the order in which we validated the technology.
Before designing a bulk-generation system on top of Figma, we should have implemented just the minimum functionality needed to confirm whether our target scale would actually work.
We could not have changed the platform’s limitations, but we could have discovered them sooner.
Would the outcome have been different if we had done both of those things properly?
Honestly, probably not by much.
We could not have prevented the spin-off, and the organizational restructuring was not based on the completeness of our work.
Still, if I encountered a similar situation again, I would not respond to organizational uncertainty by focusing solely on making the code more complete.
If I Were to Build Another Design System
There are three principles I have carried from this experience into later projects.
- Before implementing components, validate the needs of the teams that will actually use them and the technical limitations of the platform. Even a simple validation build before full development begins can reveal a great deal.
- Include adoption work in the development schedule. If you wait until after the demonstration to bring user teams and decision-makers on board, it is already too late.
- Do not leave rules only in documentation. Build mechanisms such as tests, linting, and deployment pipelines that automatically detect when those rules are broken.
When an organization disappears, the code disappears with it.
The repository may remain, but a repository that no one ever opens is as good as gone.
These three principles, however, stayed with me rather than in the repository.
I have continued to draw on them whenever they are needed in later projects.
This was the story of a design system that failed.
Plenty of people have already written about successful ones.
It seemed worth leaving behind at least one story like this as well.
Top comments (0)