The traditional software testing philosophy emphasizes meticulous planning, building a robust test suite before coding begins. This approach resembles a sturdy fortress, offering protection from unforeseen threats. However, as design systems (DS) morph into intricate, interconnected networks, this rigid methodology reveals its limitations.
The Challenge of Complexity
Imagine a DS as a living ecosystem. Its components, each interacting with countless others, can create unpredictable combinations as the system grows. While valuable for isolating components, a purely proactive testing strategy struggles to anticipate the myriad of permutations that emerge. This becomes even more critical with DS like Adobe Spectrum, which offers tokens for dynamic screen density or contrast.
You inject the design system into the very bloodstream of the whole product portfolio—as a general rule, it’s a bad idea to inject crap into the bloodstream.
Josh Clark – Ship Faster by Building Design Systems Slower
The never-ending possibilities of composable design systems seem like an impossible obstacle to shipping bug-free, battle-tested components, and to some extent, it is… The next best thing is to aim for bug-free someday, focusing on the process that’ll get you always closer to perfection, with consistency and confidence.
In his article on Subcomponents, Nathan Curtis argues that complex and configurable components are brittle and difficult to test, which is true, it’s definitely easier to test smaller and simpler subcomponents separately, but when it comes to ensuring long-term robustness, having many smaller pieces that can be combined in endless ways doesn’t sound like a simpler option…
Enter Antifragility
This is where the concept of antifragility, popularized by Nassim Nicholas Taleb, becomes relevant. Antifragile systems don’t just withstand shocks; they thrive on them. Translated to DS, it means embracing the inevitable changes and complexities, using them to strengthen the system.
Antifragility is beyond resilience or robustness. The resilient resists shocks and stays the same; the antifragile gets better.
Nassim Taleb – Antifragile
Reactive Testing: Embracing the Chaos
Reactive testing, often dismissed as haphazard, aligns perfectly with this antifragile mindset. It prioritizes addressing issues as they arise. When a bug surfaces, a test is written to prevent its recurrence. This might seem counterintuitive, but it’s akin to strengthening a structure after an earthquake. Once a challenge has been overcome, the system becomes stronger, able to anticipate and resist it forever.
Reactive testing isn’t a magic bullet. A strong foundation of unit and integration tests remains crucial for core functionality. However, for the intricate web of interactions that define modern DS, reactive testing acts as a powerful complement. By learning from failures and constantly adapting the test suite, we create a system that thrives on adversity, not merely survives it.
The gold standard for DS testing is automated visual regression testing within your CI pipeline. This approach continuously verifies all components, their variants, and potential combinations across new pull requests, ensuring changes reflect your intentions.
Beyond the Basics: Revenge.css
Revenge.css, another “old school” solution often overlooked, offers a cost-effective way for DS teams to catch errors in live applications. The idea is simple: write CSS selectors for undesired styles, combinations or variants, highlighting them visually with a fix or requirement within the code.
For instance, a rule could prevent two primary buttons appearing side-by-side. Documentation can specify this, but not everyone reads it. Designers might forget, new team members might be unaware, or product managers might request changes without UX involvement. Regardless, if double primary buttons trigger a big red outline, someone will notice. Now, the focus becomes understanding the issue and resolving it, helping whoever encounters the issue fix it quickly and move on with their work.
The applications are very varied, one I like particularly is accessibility: organizations don’t always understand that it’s not a one-time thing that you can ever be done with. Whenever you create new features, you’re better off ensuring they’re accessible from day 1, but this requires training, tooling, and time. But with revenge.css, you can easily detect and provide solutions to the most common issues in your products and educate designers and developers on the matter.
I have one in the codebase I work on to ensure that custom icons that have tooltips have the proper role="img"
attributes to ensure assistive techs vocalize these properly to blind users for example, and it’s caught bugs quite a few times and prevent users from being presented with partial information that would have disabled them from getting their jobs done.
Organizations when they encounter accessibility often feel like fighting an hydra. It’s the project that they have to do and when they get to the end of the project they realize not only are they not done but there is even more work left to do.
Dylan Barrell – Agile Accessibility for Good
If this sounds interesting to you, I highly suggest checking out Testing HTML with Modern CSS by Heydon Pickering, he created the first revenge.css repository 10 years ago and explores in this article useful techniques, new capabilities with CSS3, examples and so much more.
Manual testing, reviews, empathy…
I’ve started with technical solutions, not because they should be the single way to address bugs and testing, but rather because they should be one more tool in your toolkit, that should include processes like reviews, individual efforts that come from understanding the importance of shipping quality assets and what that means, guidelines, etc.
Each Important Function is Supported by Many Elements
Permaculture Principles
Shipping robust assets is an essential part of building trust and providing teams and organizations with a good design system, as such ensuring that bugs get fixed and don’t occur again is a critical aspect of working on a Design System. Of course, that’s not it : you need to detect bugs, enable your consumers to report them, and a lot more, I suppose this will be a topic for another article some day.
The world of DS is defined by rapid evolution and unexpected challenges. A rigid, proactive testing approach can be like trying to control a wildfire. Reactive testing, on the other hand, allows us to harness the energy of unexpected changes and build a system that is not only resilient but also adaptable. By embracing the antifragile nature of design systems, we create products that go beyond good enough, becoming truly exceptional, by accepting that bugs will occur, we can craft processes that actually move our assets closer to bug-free.