It is good practice to extract sharable code into libraries.
We all get this on some level. For example, UI teams often consume a central design system to reuse UI components that encapsulate a design system's style and behavior specifications.
However, there are three ways we often miss out on extracting sharable code:
By narrowing our sense of sharable code to a very specific thing (e.g. a design system)
By failing to separate generic code from application-specific code (e.g. creating a single
GroceriesDropdown
component instead of separating a genericDropdown
component from aGroceriesDropdown
that merely wraps a generic component and plugs in any business logic--like sourcing a list of groceries)By failing to stage library "candidates" (e.g. staging a design system candidate in a consuming application)
In this article, I'll talk about the third way of missing out.
Whenever a library of shareable code exists, there is always the potential of creating too large a chasm between the shared library and the consuming applications.
For example, a central design system codebase may be closed off from contribution by the developers working on one of the applications that consumes it.
Regardless of whether the centralized, shared library allows direct contribution from the consuming applications (e.g. creating a PR on the GitHub repo), I'd argue that all consumers of the shared library should contribute.
So, what does contribution look like if the consuming applications do not contribute directly?
Maybe it's jumping on a video call with the maintainers of the shared library and saying, "Hey! I made this function/component that could fit really nicely into the shared library. Here's how we're using it: [insert GitHub link to consuming application's codebase]."
In other words, the consuming application can stage "candidates" for the shared library. The consuming application can call out useful functions/components/patterns that could be extracted into a shared library, making these candidates easily discoverable and shareable.
For example, let's say there is a central design system at a company, and there is an application that consumes it.
The design system doesn't have a Action
component, a clickable icon that animates into a background color when hovered. However, the consuming application is implementing this pattern over and over.
Without a candidate pattern, this Action
component could get nested within the consuming application and located in a manner that does not make it discoverable:
/* pages/groceries/index.js */
function Action() { ... }
There exists in the consuming application a generic component that could be shared by any application, and hence, could be extracted into the central design system.
However, if no one on the team has an awareness that they can contribute to the design system (even if indirectly), then the shareable component will never get discovered--there won't be a pattern instilling an intentionality to contribute to the design system.
However, with a candidates pattern, we can instill an intentionality to contribute to the design system by having a pattern for stashing code that could be extracted into a central library.
So, how can one implement a candidates pattern? It's quite simple. Segregate a folder in the consuming application that is detected to stashing shared code that could be extracted.
For example, you could stash the design system candidates into a components/design/lab
folder in the consuming application(s).
Then, when an opportunity arises, you can easily share the component within that folder with the maintainers of the design system.
Finally, as a cherry on top, you can do two more things:
- Alias the dedicated "candidate" folder to match the external package with
/lab
appended:
components/design/lab --> @mycompany/design/lab
By creating an alias, you can treat the candidate folder as a shared package, making it easy to change the import if it does get extracted.
- Write documentation to explain the candidate pattern and enforce it in code reviews.
Top comments (0)