Frontend components, be it actual UI components or services used by the client, are inevitable in any web application project. We don’t like to write and maintain duplicated code for obvious reasons.
In many cases the frontend guild may find itself maintaining many components which are scattered across the code base, or perhaps have their components reside on a dedicated directory called “components” in which you can find all the components the client code uses, from a simple button to complex registration form.
So… what’s wrong with that?
When the components are scattered on different parts of the code base the chances of one team creating or overlapping the same component another team has already created is very high. While each team is concerned with its scope, the transparency of what components the entire project has available is hard to get.
So you say, “ok, let’s put all our components in a single directory”...
Having your entire components reside in a single directory is also not the best solution. Given that this directory is part of your monolith there is nothing preventing these components from “reaching out” beyond the boundaries of their scope and form dependencies which will cripple future refactoring.
An example can be a component which relies on the fact that there is a variable it needs on the global scope, or importing a service from a “nearby” module’s directory.
Adding to that is a scenario where you would like to separate your big application into small, separated microfontends and the requirement for reusable shared components rises. In this case, having to extract the components from the monolith into a shared library becomes a real burden caused by the “unhealthy” dependencies described in the previous paragraph.
Of course, unless you work to tweak your CI process, any change to the application code will also trigger the entire test suite for the components, which might not be required most of the time. This adds significant time to the CI process, and in turn slows down the feedback developers so desperately need to be efficient.
Also, for this reason, testing the components in an isolated environment with no fear of side effects becomes much harder to do.
So it's no surprise that almost every RnD organization reaches this realization at a certain point in its evolution - components used by the frontend should be managed better than just being scattered around on the code base or being “dumped” into the trash bin called “components directory”.
Separated repository for your components
One of the common solutions for the issues mentioned above is extracting your components into a separated code base - a different repository altogether.
This repository hosts components only and through a build process it publishes a package containing the entire components.
This repository will enable you to eliminate a few issues -
You will no longer worry about a component depending on the main project services and modules since it simply cannot reach them. This will create a better design for your components, not relying on side effects and global data but rather have this data injected to them (be it props or through service references that the component can use via a known interface).
Another plus is having the ability to test your components in an isolated autonomous environment. You can run the component’s library tests in separation from the main project’s build process, which will dramatically reduce the time spent on these tests and increase the developers velocity.
Having tools like StoryBook can be applied in a more precise manner, including only what the library exposes and perform automation tests using tools like Cypress.
The separated build process for this repository will complete in publishing a new version of the components library. A version that the main project (and other micro frontends) can now use, and even revert to a previous version if something goes wrong.
Having the ability to just revert the components version and not the entire project’s commits give more flexibility and precision in handling production bugs.
But does this solve all the component maintenance issues we have? Not really.
We still have a big pile of components, some are small building blocks while others are more complex, all in the same package, having the same version, treated with the same measures for quality.
Can a Button component which is used many times in different components be treated the same as a Cart component which has only a few usages?
Components Types
Yes, not all components were born equal.
If you look at your code base you will quickly identify that you have at least 2 types of components:
- Core / Building Blocks Components
- Business Components
Core / Building Blocks components
These are the components which are the basic building blocks of your system.
We’re talking about buttons, dropdowns, text fields, HTTP client service, toggles, checkboxes etc.
The common ground for all these components is that they can be published tomorrow as an open source project for any company to use, and the fact that it was created by your company has no effect on it whatsoever.
These components can be handed a theme and work with it, and this is how you adjust it to be aligned with your company’s theme, but this theme can be changed and match any other style guidelines.
These components, since they are frequently used throughout the system, must adhere to a very strict quality standard. Their unit test coverage should be reassuring. Their automation tests (those which require one, not just a Button click) and visual regression tests coverage should be very high, since any change in their appearance will affect the entire product you’re building.
It is important to emphasize that Core components do not mean only basic building blocks. They mean any reusable code that does not rely on your product business logic. Yes, these components can be complex as well.
Business Components
These are the components which are meant to be used only under your company’s product since they rely on certain conditions that are kept by it. In there you may find components like RegistrationForm, Footer or maybe services which rely on your server’s implementation like “Logger” etc.
These components tend to be less frequently used than the Core components and regressions to them, in most cases, will not have the same vast impact as with the Core components. It does not mean that you should be lightheaded with them, but yes, they can have a more “loose” treatment.
These components are obviously dependent on the Core components.
Someone said “Monorepo”?
Well, a Monorepo is a good solution for maintaining your components libraries.
You will have a monorepo holding your Core and Business components in different packages, having the Business components package depend on the Core components package and letting tools like Lerna or NX take care of publishing their versions to your preferred NPM registry.
You can read more on how to get started with a Monorepo in this series of articles.
Wrapping Up
Separating your components from the main code base, and dividing them into types will allow you a better control on the quality, versioning and refactoring of these components.
This will also make your main CI process much faster, for it no longer needs to run all the tests and build steps for your components when nothing has really changed for them. This will be handled by a different CI process dedicated to your components, and probably one that will not run as often as your main CI. It will also not halt the entire development team while a certain component breaks the CI.
All-in-all I believe that extracting your components this way is an inevitable step for frontend organization if it wishes to scale fast and be agile in the process.
As always if you have another idea on how to better maintain your components, or you don’t agree with the ideas brought here, I would love to hear about it, so make sure you share it in the comments section below :)
Hey! If you liked what you've just read check out @mattibarzeev on Twitter 🍻
Photo by Rick Mason on Unsplash
Top comments (0)