Photo by Liam Henry on Unsplash.

How To Create a Component Library From a Monolith – A Case Study

It is needless to say that tools and frameworks for frontend web development now a days emerges almost at exponential speed.

Today we use package managers (NPM), bundlers (webpack), transpilers (Babel), templating engines (Pug), language extensions (SCSS), single page application frameworks (Angular, React, Vue), and so on. All with the ambition to improve and speed up web development processes on the frontend; ten years ago, almost none of these existed.

In this case study I will describe how we, a team of frontend developers, utilized these new technologies as we helped a client to create a component library from their existing monolith; a monolith originating prior to the web development tools bonanza. You will get a first hand view of the problems we faced, how we got buy in from the organization, and how we kept a steady phase creating and migrating to our new solution.

I will highlight how a component library can speed up your development cycles, what benefits the organization can expect, and what workflow to use. At the end you will hopefully have gained the insights needed to do a similar migration yourself, or simply create a pattern library from scratch (without passing the monolith first).

Note: The content is slightly skewed towards web developers and UX-designers, but product owners and managers will probably get the gist of it anyway.

Before we start, what is a component library and a monolith?

A component library, or pattern library, is a set of recurring user interface design elements (components), such as buttons, dropdowns, modals, etc.

The main purpose of a component library is to provide “off the shelf” solutions to recurring design elements, in order to speed up the development process and keep a consistent look-n-feel across one or multiple projects.

Probably the most famous open source component library is Bootstrap, initially released by Twitter in 2011 (it is currently on version 4).

A monolithic application, or monolith, on the other hand is, according to Wikipedia, “…a single-tiered software application in which the user interface and data access code are combined into a single program from a single platform. A monolithic application is self-contained, and independent from other computing applications.”.

While this structure might work in some cases, sharing components embedded in a monolith with other projects is impossible.

The problems we faced

For a couple of years now the general trend in web development has been to move away from monolithic applications in favor of microservices. A microservice being a loosely coupled and standalone service, that together with other microservices make up an application. The main benefits with this kind of application architecture is to improve modularity and make the application easier to understand, develop and maintain. E.g. small, distributed, and isolated modules are much easier to iterate and test; any consumer of a microservice only need to know the interface and not the implementation details, improving development speed.

The roadmap for our client’s monolith was no different, where the goal was to split it in to smaller distributed projects (microservices) to make it more resilient. The decomposing process was well under way and one part of the application had already been broken out (Project: A), subsequent parts where soon to follow. Below is a simplified illustration of what the architecture looked like.

Illustration of how the projects were set up. Styling (CSS) and logic (JS) for “shared” components were duplicated across projects. When new projects were added, the code snippets for the “shared” components were simply copy-pasted. It was also impossible to see what components actually existed, without digging through the source code.

This new microservice approach highlighted several limitations in how current UI-components were implemented within the monolith; below are some of the most prominent problems we faced, exemplified through the button component.

  • Source code (CSS/JS) was duplicated across projects, e.g. the same button styling with accompanying logic appeared in numerous places. When adding new projects, the source code for the button component was simply duplicated.
  • Colors and themes were inconsistently used, as we had no single truth to adhere to.
  • It was impossible to get a quick understanding of what the button implementation looked like, without digging through the code base or by “inspecting” a button in the live application.
  • Designers who joined the team did not know what components were already available, accidentally creating new alterations of buttons by misstake.
  • No one new exactly what states a button could be in or how they were implemented (hover, click, focus, active, etc.)
  • When a fundamental change were to be made, e.g. moving from buttons with sharp corners to buttons with round corners, the same change had to be applied in numerous places and across projects.
  • CSS classes and hierarkis were used inconsistently since components had evolved over time, in an ever growing monolith, with many developers contributing.

All and all, working with available components within the monolith was painful and slow, both for developers as well as designers. It even lead to an inconsistent look-and-feel, both within and across our projects, providing a somewhat funky user experience. Something had to be done!

Expected outcome

To relieve ourselves from the above stated problems we decided to export the most frequently used components from the monolith into a stand alone library (having seen how well it worked with Bootstrap). But before we got to work we wrote down exactly what we wanted to achieve, to be able to measure our success. Here is a high level list of our criteria and the expected outcome:

  • Provide our most fundamental components in a stand alone library, for easier use and re-use.
  • Ensure the new library will endure over time, making sure it can be utilized even when the monolith is long gone.
  • Showcase available components to both developers as well as designers using a responsive online style guide.
  • Allow developers to easily see how components are implemented and used.
  • Allow projects to use different versions of our components, to not be automatically forced into a new version.
  • Be able to use our components regardless of build processes in individual projects.
  • Allow projects to include specific components, without having to include the entire library (leaving a much smaller footprint).
  • Keep the library agnostic from any project relying on it.
  • Provide a workflow that is both fun and easy for developers and designers to use, to ensure new components will be added.

Once all criteria were down we got go ahead from product owners and management. They were easily convinced when they realized the benefits the new library would bring mid/long term in terms of increased development speed (reduced cost of delay), consistency (enhanced brand recognition), and improved co-creation between developers & designers; the only remark was that we (as developers) had to make sure we were able to create the library without restraining the main work stream too much.

The solution

Since all of our projects already used NPM as package manager for frontend dependencies we decided that our new component library should be installable and managed using NPM as well. Having our DevOps team just around the corner allowed us to swiftly create a local repository for our library, that could be built and published to our internal NPM-registry using existing infrastructure and automated processes. Hurray for DevOps and continuous integration!

Don’t have all the fancy DevOps tools and pipelines in place? Do not despair, the outcome above can be achieved just by using regular git repositories with NPM as well.

Now was the time to decide what components to extract and what workflow to use.

To make sure we would keep a steady phase extracting our components from the monolith into our library, it was decided to take the least common denominator approach (e.g. start by extracting the smallest components we knew was used across our projects).

In the first version released, or MVP if you will, we ended up having only ten theme colors and a button (including three different layout options).

A release included both a pre-built version with all CSS and JavaScript compiled for instant usage, but also the original source code allowing single components (such as buttons) or constants (such as colors) to be imported straight away. Our components were based one Vue.js but sticking to plain vanilla JS or any other framework would work just fine, it is up to you. For all styling we relied on SCSS.

To address the issue of designers and developers not knowing what components were already available, we ended up going with a solution called Patternlab. Patternlab, apart from being based on the Atomic design system, allowed us to easily create and maintain an online style guide reflecting all available components. The Patternlab style guide also included a feature for automatically displaying the markup associated with each component along side itself. It even had a feature to display the components responsively using predefined screen sizes. Yay!

Below is an illustration of how our new microservice architecture ended up, including our new component library and its accompanying style guide. Different projects could swiftly install (depend on) which ever version of the component library suited the most, and upgrade independently of other projects. Designers and developers could easily talk about available components just by referring to the online style guide.

Illustration of the resulting architecture for our projects, component library, and style guide. Different projects could depend on different versions of the component library, whereas the style guide always displayed the latest version of our components.

One thing we noticed while extracting our components was that the actual implementations were somewhat inconsistent, between different components, in terms of naming convention and structure of CSS-classes/markup. To further improve our new library we converted all patterns upon extraction to align with the BEM (Block Element Modifier) method for organizing our CSS; this made our new components less error prone upon integration and also made them easier to maintain.

Gotchas

  • Naming collisions: The idea of keeping the new library completely agnostic from projects depending on it created some funky side effects. As the library did not introduce a specific namespace, integrating our new library into the old monolith caused some naming collisions. The work-around for this was simply to wrap the library import in a unique namespace, e.g. cl (for component library), forcing all elements in the monolith to use the prefix. With that said, the monolith could run both new and old buttons simultaneously (until all old buttons could safely be removed).
  • One style guide to rule them all: The way our style guide was released and published only allowed us to have the latest version of the library displayed online. However, it was possible to run a local version of the style guide depending on your particular release of the component library.

Style guide and library workflow

Having both a component library and a style guide in place is a great way of speeding up development cycles, as components becomes available straight “of the shelf”. However, what happens when designers create new patterns or when developers find old patterns in the monolith? Do you go ahead and add them straight into your library, or what workflow should be used?

For us, it made most sense to only migrate patterns from the monolith into the library if we knew other projects would benefit from them straight away; to not pollute the library with project specific patterns. As for new pattern suggestions by designers, the approval process looked somewhat like Cap Watkins suggestion in his post A Decision Tree for Designers:

Illustration by Cap Watkins

The only modification to the workflow illustrated above was that before we actually added any brand new components to our library and style guide, we again made sure other projects would also benefit from it. If no synergies were apparent, new components were added to the respective projects instead.

Since our style guide and the actual component library were two standalone entities we incorporated a process to make sure that every time a library release was made, a new release of the style guide accompanied it; making sure newly added components would be visualized instantly online.

The final outcome

Despite the initial workload related to doing research, creating the library, setting up the style guide, and exporting components, it was well worth the effort when summarizing the outcome. Given the fact that the two main outcomes were reduced cost of delay and improved co-creation, the migration work done will continue to pay dividend for a long time onwards.

Reduced cost of delays

  • No more duplicated work, component bug fixes and updates done in one place.
  • Fewer bugs, as components could be tested in isolation.
  • Faster release cycles with microservice architecture.

Improved co-creation

  • Clear communication between designers and developers using a standardized component nomenclature.
  • Improved development speed with “off the shelf” agnostic components.
  • Fun and iterative component refinement using a highly accessible style guide.
  • Reduced amount of design choices when designing and developing new features; sketches and concepts could quickly be drawn based on existing components.

Enhanced brand recognition

  • Consistent look-n-feel within and across projects.

Give credit when credit is due

Right now I am the one telling the story, but none of this would have been possible without my frontend colleagues at the time: Kristinn Sigmundsson, Dennis Lindblom and Carl-Gerhard Lindesvärd. All which contributed, in different ways, to the success of creating the component library and style guide.

Enjoyed this case study? Want more information?

Hope you had a good time reading this post and that you now feel confident in doing a similar improvement yourself. If you have any questions, do get in touch and I will try to help out! You can reach me and my company either through flatwave.se or by visiting our company page Flatwave on LinkedIn.


Don’t forget to follow me or Flatwave-insights to catch anything we release related to customer-value and software development! Leave your thoughts and comments, I am always interested in hearing others opinions!