DEV Community

Cover image for Design System: Compositional philosophy of components
Matuzalém Teles
Matuzalém Teles

Posted on

Design System: Compositional philosophy of components

Products evolve quickly within a large organization, companies need to move fast, build consistently, deliver new products and maintain existing ones. As part of all this, the solution adopted is to build a Design System, rooted in the principles of common patterns, colors, typography and grid.

The great challenge of a team that deals with the materialization of the design system into components is how to expose the fast pace of a company and continue to deliver value to the components for the product teams. An organization's developers want to go beyond implementation because products evolve, but some of them just want to follow the implementation.

There is a big challenge in this environment, the Design System team on the Design side can take different approaches, arrest Design to specific component cases or create just the foundation (e.g Colors, Typography, spacing, Grid, Layouts...) or meet both cases. There are disadvantages and advantages in each case and it is up to you to understand how each case can work better in the context of your organization.

On the other hand, developers of the component library can take different approaches:

  1. Create components providing only the cases of the Design System, restricting the use of the component to cases other than the one defined.
  2. Create components with high flexibility, allowing developers to deviate from the defined cases when the product design thinks beyond what is defined.

The result of this can be bad on both sides, we can frustrate the developers because they may have to create their own component or they will have to do a lot of work with the flexible components to arrive at the specific case of the design created by the designer of his team and the Design System can block the creative mind of the designer because the component definitions are fixed.

Correcting and dealing with this is complex, but what should we do? in our company (Liferay) in the past we have followed the approach of fixed components to the Design System and not allowing developers to go far beyond what is expected, in a company context with more than 300 engineers and several product teams, this was a bad decision, which resulted in the low adoption of components, for some reasons:

  • The components were too attached to the Design system
  • Little flexibility
  • Designers created components beyond implementation

As a result, our components had large APIs, with low usage, high configuration complexity, increasing maintenance costs and entering the depreciation phase very quickly.

We know this was a bad decision and we quickly switched to another approach the following year. We took the approach of achieving a balance between flexibility and specialized components in our component library.

Dealing with this may seem easier, but how do we materialize the idea? We follow a hybrid approach to our components, we call this the Multi-Layered API library.

Multi-Layered API library

Multi-Layered components mean that we have two ways to provide a component:

  • low-level - Basic building blocks to provide flexibility so that you can customize and create high level components.

  • high-level - Highly specific component that tend to cover only specific use cases, limiting their flexibility.

The principles are pretty basic but to be called you need to follow some laws.

Low-level

Low-level components follow the composition, such as small blocks that build a DropDown component.

<ClayDropDown />
<ClayDropDown.Action />
<ClayDropDown.Item />
<ClayDropDown.ItemList />
<ClayDropDown.Search />
Enter fullscreen mode Exit fullscreen mode

High-level

High-level components may also follow composition but may be more specific components with something in common among many teams.

<ClayButtonWithIcon />
<ClayCardWithHorizontal />
<ClayCardWithNavigation />
<ClayDropDownWithItems />
Enter fullscreen mode Exit fullscreen mode

High-level components are built with low-levels, this may decrease maintenance but increase the surface of available APIs.

The benefit of this is that you can come up with a hybrid approach that reaches more adoption and many teams with different tastes.

You can read more about our composition approach in our documentation in our component library.

The result of this approach was the high adoption of our components in different teams and products with different contexts, helping teams to deliver faster and they are happier.

This seems to solve the problems at the user level but we get involved in several discussions on how to differentiate, build and structure the low-level and high-level components. I have separated some of my thoughts on this from trying to follow a theory or something conceptual and adjusting things over time.

Tail theory

Do not confuse this with The Long Tail Effect theory.

Tail theory is a rope anology with two ends or tails, where you place both types of components, low-level and high-level, at each end. The distance between them can cause great pain or great successes is all or nothing here!

  • Extremes can be very painful or very straightforward, meaning that high-levels attached to specific use cases can bring happiness to a team that is following the definition correctly and can create a lot of pain for those who are not.
  • For those in pain, the pain gets bigger because the low-level is at the other end, building from low-level to something close to high-level can be painful.
  • Extreme high-level cases may be of little adoption as their cases are specific and do not allow any change outside of the specified.
  • Low-levels tend to have a long life because they are more flexible but naturally require more work.

More stuck components tend to change more over time and their life cycle tends to be shorter.

This graph is hypothetical, real data has not been used here, but is based on my experiences over time working with component library.

Some peculiar things: we may have low-level working very well in the short and long term and having few changes, that would be the ideal scenario for us but in the middle there is one thing we can lose, the effort and development experience: these are points keys for people to adopt library components and to build without much effort.

Very specific components can change a lot over time and in a short period of time and it may happen that at some point we will have to depreciate why the component has swelled, this can happen with any component but we will have maintenance issues and a constant fight to update things before people can start using them. We can extend the life of these components and decrease maintenance so we can worry about improving or building things beyond the components.

So imagine that if I push the component closer and closer to the middle of the rope and the distance between the sides decreases, it means that we reduce the pain in the sides but getting closer will not have a clear difference, it creates confusion. Each time we give some flexibility to the high levels we push them to the middle of the rope, the experience gets better and the pain can decrease.

Note that we do not want to join the two sides but we want to get close, the tail is the extreme and the extreme has a price, we just want to distance it and we need to offer some flexibility for the high-level components and decrease the flexibility for the low-level.

With that in mind, we can:

  • Increase the longevity of high-level components.
  • Fewer changes over time
  • As a result, we support more use cases
  • People are happier

Although the greatest benefit falls on high-level, low-level is influenced because once we take away some of its natural flexibility, it slightly increases the amount of change over time and the maintenance over it also increases but this is necessary since we have to create a balance and the disparity between the two is not stark.

I believe it is easier to stick to this theory. Once we understand, it will be natural to identify when a component needs more flexibility or when we need to maintain the API.

Our Liferay component library is Open Source and you can access it through Github:

I have been working on this for 2 ½ years and I will be very happy to hear your thoughts and experiences.

Our Github repository is full of very interesting thoughts and speeches. Explore our issues and PR's 🙂.

Follow + Say Hi! 👋 Connect with me on Twitter 🐦!

Top comments (0)