How do you decide what belongs in the system vs in a single product?

On this page

A pattern belongs in the shared system only when it is genuinely common across products and stable enough to standardize, and it stays product-local when it serves one context or is still changing. The test is two questions answered together: is this shared, and is it stable. Shared-and-stable goes up into the system. Specific-and-evolving stays down in the product. Anything that fails either question does not yet earn a place in the shared layer, no matter how clean it looks in isolation.

The instinct to resist is the one that pushes everything into the shared system because it might be reusable. That instinct feels responsible, like good engineering, but it pollutes the system with one-off needs. A component built for a single product’s edge case, lifted into the shared library on the theory that someone else might want it, becomes a liability the moment no one else does. Now every team inherits a thing they did not ask for, the maintainer carries props and variants that exist for one consumer, and the system’s promise, that what is in it is trustworthy and broadly useful, quietly erodes. Reusability is not a property you grant by relocation. It is something a pattern demonstrates by actually being used in more than one place.

Consider a date picker versus a tax-estimate widget. The date picker shows up in three products, behaves the same way in each, and its design has not meaningfully changed in a year. That is shared and stable, and it belongs in the system where every team can rely on one accessible, tested implementation. The tax-estimate widget lives in one product, encodes that product’s specific calculation logic and layout, and gets revised every quarter as the rules change. Promoting it would force a general API onto something inherently specific and would chain the system’s release cadence to one product’s churn. It stays local. The same logic separates a generic empty state, which belongs up, from a product’s onboarding checklist, which is specific to that product’s flow and still being tuned.

One limit is worth stating: this moves over time, and that is expected rather than a failure. A pattern can start product-local, prove itself across two or three teams, settle down, and then graduate into the system once it has earned both halves of the test. The reverse also happens: something in the system that turns out to serve only one product, or that keeps changing because it was promoted too early, should be pulled back down rather than defended. The exception worth naming is the deliberate primitive. Sometimes a team puts a low-level token or layout primitive into the system before it is widely used because it is foundational and cheap to standardize, and that can be right. But that is a judged bet on a stable foundation, not a blanket license to hoist every component upward on the chance it travels.

Run the two-part test before promoting anything. Ask whether the pattern is truly shared across products today, not whether it could be, and ask whether it is stable enough that standardizing it will not trap consumers in constant migration. Promote only what passes both. When a pattern is borderline, let it live in the product, watch whether other teams reach for it, and graduate it once the evidence is real. Keep the specific and the evolving local, and keep the system populated only with what others genuinely share, so that being in the system continues to mean something the whole team can trust.

Leave a comment

Your email address will not be published. Required fields are marked *