Title: Openai/695d28c7-94c8-8006-836b-96dbe559b5eb - freem URL Source: https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb Markdown Content: _This conversation was exported from ChatGPT_[[1]](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_note-1) ## gpt-5-2-thinking[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=1 "Edit section: gpt-5-2-thinking") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=1 "Edit section's source code: gpt-5-2-thinking")] ### User: This is a question from Software Engineering Stack Exchange:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=2 "Edit section: User: This is a question from Software Engineering Stack Exchange:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=2 "Edit section's source code: User: This is a question from Software Engineering Stack Exchange:")] This is a question from Software Engineering Stack Exchange: ’m working on a very large frontend project organized as a single monorepository. We have a components/ directory that contains 30+ shared UI components. These components are used across a large number of features, and many independent teams work on different features in parallel. The core problem: Any change to a shared component can potentially cause regressions across the entire product, which makes QA extremely expensive and slow. Because of this, teams are afraid to modify shared components, even when improvements or fixes are needed. We must keep a single repository (splitting shared components into a separate repo is considered a bad option due to the number of teams and coordination overhead). Different teams should be able to work independently without blocking each other. Changes to shared components must not trigger full-product regression testing by default. The solution should scale to dozens of components and teams over time. I’m looking for architectural patterns or proven approaches to organizing shared UI components in a large monorepo. I stand with versioning of components (can have Button, Button.v2 Button.v3 and etc), what are the pros and cons I might encounter? I’m especially interested in real-world approaches used in large-scale frontend systems, not just theoretical ideas. If helpful, the frontend stack is React + TypeScript, but I’m more interested in architectural patterns than framework-specific solutions. Any guidance, references, or examples would be greatly appreciated. ### Assistant: You can make this scale in a monorepo, but you usually need two things at the same time:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=3 "Edit section: Assistant: You can make this scale in a monorepo, but you usually need two things at the same time:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=3 "Edit section's source code: Assistant: You can make this scale in a monorepo, but you usually need two things at the same time:")] You can make this scale in a monorepo, but you usually need two things at the same time: 1. A real “package boundary” for the shared UI library (or libraries) so consumers do not silently pick up breaking changes. 2. A CI strategy that only tests what is affected, plus strong component-level automated checks, so you stop paying “full product QA” for every change. Below are patterns that actually work in large React + TS monorepos. #### What it is: Keep one repo, but model components/ as one or more workspace packages, published to an internal registry (or at least versioned and consumed like packages). Use semantic versioning and a release tool (Changesets is a common choice in TS monorepos). changesets-docs.vercel.app[[2]](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_note-2)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=4 "Edit section: What it is: Keep one repo, but model components/ as one or more workspace packages, published to an internal registry (or at least versioned and consumed like packages). Use semantic versioning and a release tool (Changesets is a common choice in TS monorepos). changesets-docs.vercel.app[2]") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=4 "Edit section's source code: What it is: Keep one repo, but model components/ as one or more workspace packages, published to an internal registry (or at least versioned and consumed like packages). Use semantic versioning and a release tool (Changesets is a common choice in TS monorepos). changesets-docs.vercel.app[2]")] Key property: A feature team only gets a component change when they choose to bump the dependency. ##### - Teams work independently: Component changes do not immediately affect every consumer. Consumers update on their timeline.[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=5 "Edit section: - Teams work independently: Component changes do not immediately affect every consumer. Consumers update on their timeline.") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=5 "Edit section's source code: - Teams work independently: Component changes do not immediately affect every consumer. Consumers update on their timeline.")] * No default full regression: Your default is “component tests + affected consumer tests,” not “entire product QA.” * Scales to many teams: Versioning + changelog + deprecation policy becomes the coordination mechanism. ##### - packages/ui (design system components)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=6 "Edit section: - packages/ui (design system components)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=6 "Edit section's source code: - packages/ui (design system components)")] * optionally packages/ui-tokens, packages/ui-icons, packages/ui-utils * apps/features depend on @org/ui with a pinned semver range * updates happen via automated “dependency bump” PRs (Renovate-style workflow) ##### - Blast radius control (biggest win)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=7 "Edit section: - Blast radius control (biggest win)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=7 "Edit section's source code: - Blast radius control (biggest win)")] * Clear contracts: public API surface becomes explicit * Natural deprecation/migration: old versions can live while teams migrate ##### - You are signing up to maintain versions (release notes, deprecations, support window)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=8 "Edit section: - You are signing up to maintain versions (release notes, deprecations, support window)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=8 "Edit section's source code: - You are signing up to maintain versions (release notes, deprecations, support window)")] * Local dev ergonomics can be trickier if you are not using workspace tooling well * Potential for version skew: different parts of the product on different UI versions (you mitigate this with “supported versions” policy and scheduled migrations) #### Even with package boundaries, you still want to avoid massive CI runs.[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=9 "Edit section: Even with package boundaries, you still want to avoid massive CI runs.") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=9 "Edit section's source code: Even with package boundaries, you still want to avoid massive CI runs.")] Tools like Nx compute “affected projects” (changed projects plus dependents) so CI only runs the relevant builds/tests. Nx[[3]](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_note-3) In Bazel ecosystems, the same idea exists via dependency queries and “diff-to-affected-targets” tooling (for example, bazel-diff). GitHub[[4]](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_note-4) Reality check: if you change a core Button that everything depends on, “affected” might still be huge. That is not a tooling failure, it is telling you the truth about coupling. The fix is usually more boundaries (split packages) and/or more backward compatibility. #### If the fear is “we will break unknown screens,” you need fast, trustworthy signals closer to the component library:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=10 "Edit section: If the fear is “we will break unknown screens,” you need fast, trustworthy signals closer to the component library:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=10 "Edit section's source code: If the fear is “we will break unknown screens,” you need fast, trustworthy signals closer to the component library:")] * Storybook as the contract surface * Visual regression on stories (Chromatic, Percy, etc.). Chromatic integrates with Storybook and adds PR checks for UI changes. chromatic.com[[5]](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_note-5) * Component unit tests (Testing Library), accessibility checks (axe), and type-level API tests (for TS public props) This does not eliminate all integration risk, but it dramatically reduces “manual QA for every tweak.” #### In big orgs, the technical setup fails without governance:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=11 "Edit section: In big orgs, the technical setup fails without governance:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=11 "Edit section's source code: In big orgs, the technical setup fails without governance:")] * Component owners/maintainers (not necessarily gatekeepers, but accountable) * Backward-compatibility rule by default * Breaking-change RFC and a defined support window * Deprecation workflow: warn in console/dev builds, codemods, migration guide * “No breaking changes on Friday” style operational rules if needed This is how you keep velocity without making the shared library radioactive. ### This is a real pattern, but it has sharp edges.[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=12 "Edit section: This is a real pattern, but it has sharp edges.") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=12 "Edit section's source code: This is a real pattern, but it has sharp edges.")] ##### - Zero-risk rollout for consumers: old stays old, new opts in[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=13 "Edit section: - Zero-risk rollout for consumers: old stays old, new opts in") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=13 "Edit section's source code: - Zero-risk rollout for consumers: old stays old, new opts in")] * Great for big redesigns where compatibility would be messy * Good when migration needs to be gradual across many teams ##### - Permanent maintenance tax: bug fixes, a11y fixes, and behavior changes now have to be duplicated or backported across variants[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=14 "Edit section: - Permanent maintenance tax: bug fixes, a11y fixes, and behavior changes now have to be duplicated or backported across variants") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=14 "Edit section's source code: - Permanent maintenance tax: bug fixes, a11y fixes, and behavior changes now have to be duplicated or backported across variants")] * Inconsistent product UX: you end up with mixed button styles/behaviors across the app * Harder to remove old versions: teams stop prioritizing migrations because “it still works” * API fragmentation: docs, examples, and developer intuition get worse over time ##### Instead of Button.v2 as a naming convention, prefer one of these:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=15 "Edit section: Instead of Button.v2 as a naming convention, prefer one of these:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=15 "Edit section's source code: Instead of Button.v2 as a naming convention, prefer one of these:")] * Package major versions (@org/ui@1 vs @org/ui@2) with semver and a support window (cleaner contract) * New component name when semantics change (PrimaryButton/Button2 is not great, but sometimes Button vs ActionButton is legitimately clearer) * Adapter layer: keep Button as a thin wrapper over ButtonNext during migration #### If I had to pick one approach that matches your constraints:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=16 "Edit section: If I had to pick one approach that matches your constraints:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=16 "Edit section's source code: If I had to pick one approach that matches your constraints:")] 1. Make shared UI a workspace package (or a few packages) with semantic versioning (Changesets is a common workflow). changesets-docs.vercel.app[[6]](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_note-6) 2. Consumers pin versions and update via automated bump PRs. 3. Require Storybook stories for every component and run visual regression on PRs. Storybook[[7]](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_note-7) 4. Use affected-only CI so the default pipeline is fast. Nx[[8]](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_note-8) 5. Establish a deprecation and support policy (for example: “we support the last 2 major UI versions; older majors get security/a11y fixes only, then are removed on a schedule”). That combination gives you independence, reduces QA cost, and scales socially. If you want, I can sketch a concrete repo layout + CI pipeline outline (GitHub Actions style) for this setup, including how teams consume a pinned UI version while still developing locally against workspace packages. ### User: Is the actual problem that they're using a monorepo?[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=17 "Edit section: User: Is the actual problem that they're using a monorepo?") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=17 "Edit section's source code: User: Is the actual problem that they're using a monorepo?")] Is the actual problem that they're using a monorepo? ### Assistant: Not really.[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=18 "Edit section: Assistant: Not really.") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=18 "Edit section's source code: Assistant: Not really.")] Not really. A monorepo can make it easier to accidentally ship breaking UI changes everywhere at once, but the underlying problem is usually: * No real boundary between “library” and “consumers.” If every app/feature imports shared components as “just another folder,” any change is instantly live everywhere. * No release/compatibility contract. Without semantic versioning (even inside a monorepo), there’s no “opt in” moment for consumers. That’s what forces “test the whole world” every time. * Insufficient automated signals at the component layer. If you don’t have strong Storybook coverage, visual regression, a11y checks, and interaction tests, the only way to be confident is broad manual regression. * Coupling is too high. A Button used by everything should require a lot of confidence to change. If it’s not designed for backward compatibility, every tweak becomes a product-wide risk. A multirepo can hide this temporarily (because updates don’t propagate automatically), but it doesn’t fix the root issue. It just forces a boundary by making dependency updates explicit. You can get the same benefits in a monorepo by treating shared components as versioned packages (workspace package + semver) and adopting an “affected-only” CI strategy plus strong component-level regression testing. So: the monorepo isn’t the problem; the lack of boundary + contracts + tests + upgrade mechanics is. ### User: Do your suggested changes fix those problems, or do they need to step back and actually make enforceable contracts?[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=19 "Edit section: User: Do your suggested changes fix those problems, or do they need to step back and actually make enforceable contracts?") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=19 "Edit section's source code: User: Do your suggested changes fix those problems, or do they need to step back and actually make enforceable contracts?")] Do your suggested changes fix those problems, or do they need to step back and actually make enforceable contracts? ### Assistant: My suggestions only work if they create enforceable contracts.[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=20 "Edit section: Assistant: My suggestions only work if they create enforceable contracts.") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=20 "Edit section's source code: Assistant: My suggestions only work if they create enforceable contracts.")] My suggestions only work if they create enforceable contracts. The tools (workspace packages, semver, “affected” CI, Storybook/visual tests) are just the mechanics that make a contract real. If they only “reorganize folders” inside the monorepo, nothing is fixed. #### At minimum, it has to be true that:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=21 "Edit section: At minimum, it has to be true that:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=21 "Edit section's source code: At minimum, it has to be true that:")] 1. There is a defined public API surface * Only exported components/types are allowed for consumers. _Consumers cannot reach into internal files (components/Button/internal/_). * Styling tokens, CSS variables, DOM structure expectations are either part of the contract or explicitly not. 1. Breaking changes are detectable and gated * A change that would break consumers requires an explicit “major” bump. * PR checks can fail if someone introduces a breaking change without bumping appropriately. 1. Consumers can opt in * They don’t automatically get breaking changes “because main changed.” * Upgrading is an explicit dependency bump (even inside a monorepo). 1. There’s a support/deprecation policy * You can’t keep Button.v1 forever. You need a window: “2 majors supported” or similar. #### ###[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=22 "Edit section: ###") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=22 "Edit section's source code: ###")] Yes — this is the core contract mechanism. It makes upgrades explicit and creates a place to put “this is breaking” vs “this is safe.” Without it, you’ll always have fear-driven QA. ##### Helpful, but it’s not the contract. It just prevents wasting time when changes are localized. If you change a widely-used component, “affected” will still be big, and that’s correct.[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=23 "Edit section: Helpful, but it’s not the contract. It just prevents wasting time when changes are localized. If you change a widely-used component, “affected” will still be big, and that’s correct.") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=23 "Edit section's source code: Helpful, but it’s not the contract. It just prevents wasting time when changes are localized. If you change a widely-used component, “affected” will still be big, and that’s correct.")] ##### This is how you validate the contract cheaply. It reduces the need for manual full-product QA for non-breaking changes. But it doesn’t replace versioning.[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=24 "Edit section: This is how you validate the contract cheaply. It reduces the need for manual full-product QA for non-breaking changes. But it doesn’t replace versioning.") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=24 "Edit section's source code: This is how you validate the contract cheaply. It reduces the need for manual full-product QA for non-breaking changes. But it doesn’t replace versioning.")] So the real answer is: they need contracts, and the suggested architecture is how you enforce them. #### If they want the shortest path to “teams stop being afraid”:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=25 "Edit section: If they want the shortest path to “teams stop being afraid”:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=25 "Edit section's source code: If they want the shortest path to “teams stop being afraid”:")] 1. Turn `components/` into a package (@org/ui) with strict exports * Enforce with lint rules like “no deep imports” and TS path restrictions. * Treat anything not exported as private. 1. Adopt semver releases inside the monorepo * Every component change comes with a patch/minor/major classification. * Consumers update by bumping the package version (can be automated PRs). 1. Define “what is breaking” Examples that are usually breaking: * removing/renaming props * changing default behavior in a user-visible way * changing DOM in a way tests or CSS selectors rely on * token changes that alter appearance beyond tolerance 1. Add fast regression at the component boundary * Visual snapshots on every story (PR-gated) * a11y checks * interaction tests for key components 1. Set a deprecation window * “We support the last 2 majors.” * Old majors get only critical fixes (security/a11y), then get removed on a schedule. #### If their “shared components” are actually a grab-bag of UI + business rules + feature-specific behavior, contracts will keep failing. In that case, they need to split the library (still in the monorepo) into:[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=26 "Edit section: If their “shared components” are actually a grab-bag of UI + business rules + feature-specific behavior, contracts will keep failing. In that case, they need to split the library (still in the monorepo) into:") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=26 "Edit section's source code: If their “shared components” are actually a grab-bag of UI + business rules + feature-specific behavior, contracts will keep failing. In that case, they need to split the library (still in the monorepo) into:")] * pure primitives (Button, Input, Modal) * composed design-system patterns (FormField, DataTable wrapper) * feature-level components (should live with the feature) That’s also contract work: deciding what’s truly shared. If you want, I can write what a minimal contract doc looks like (“UI library compatibility policy”) plus the CI gates that enforce it. ### User: Show me that.[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=27 "Edit section: User: Show me that.") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=27 "Edit section's source code: User: Show me that.")] Show me that. ### Assistant: Below is a “minimal but enforceable” contract doc you can drop into the repo e.g., packages/ui/COMPATIBILITY.md, follow…[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=28 "Edit section: Assistant: Below is a “minimal but enforceable” contract doc you can drop into the repo e.g., packages/ui/COMPATIBILITY.md, follow…") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=28 "Edit section's source code: Assistant: Below is a “minimal but enforceable” contract doc you can drop into the repo e.g., packages/ui/COMPATIBILITY.md, follow…")] Below is a “minimal but enforceable” contract doc you can drop into the repo (e.g., packages/ui/COMPATIBILITY.md), followed by concrete CI gates (GitHub Actions + a couple config snippets) that actually enforce it. #### UI Library Compatibility Policy (Contract)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=29 "Edit section: UI Library Compatibility Policy (Contract)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=29 "Edit section's source code: UI Library Compatibility Policy (Contract)")] # UI Library Compatibility Policy (Contract) Applies to: @org/ui (and any sibling packages like @org/ui-tokens) Goal: Component teams can ship improvements without forcing full-product regression testing, while consumer teams can upgrade on their schedule with predictable risk. ==== 1) What is Public API vs Private ==== ===== Public API ===== Only these are considered supported and stable: * Package entrypoints exported from: * @org/ui (root) '' @org/ui/ ''only if explicitly listed in package.json#exports* * Types re-exported from public entrypoints. * CSS variables / design tokens explicitly documented in @org/ui-tokens. * Storybook stories are the canonical behavioral + visual examples for public components. ===== Private API (NO consumer dependencies) ===== Consumers must NOT import: * deep paths like @org/ui/src/... or @org/ui/components/... * any file not reachable via package.json#exports * internal DOM structure (e.g., .kebab > div:nth-child(2) selectors) * internal class names (unless explicitly documented as stable) * test ids unless explicitly documented as stable (prefer role/label-based selectors) Breaking this rule means the consumer owns the breakage risk. ==== 2) Semantic Versioning Rules ==== We use SemVer for @org/ui: ===== Patch (x.y.Z) ===== * bug fixes that do not change intended behavior * performance improvements * internal refactors * docs/story fixes * a11y improvements that do not change visual layout materially ===== Minor (x.Y.z) ===== * new components * new optional props with safe defaults * new variants/slots that do not change existing default rendering * additive token additions ===== Major (X.y.z) ===== Any change that can reasonably break consumers or change user-visible behavior by default: * removing or renaming exports, components, props, events * changing default prop values in a user-visible way * changing DOM in a way that breaks common integration (e.g., removes an element frequently targeted by consumer CSS) unless explicitly declared private * significant visual change outside defined tolerances * token changes that alter appearance beyond tolerances ==== 3) Definition of “Breaking” (Practical) ==== A change is breaking if it causes any of these: * TypeScript compile errors for consumers * runtime errors for consumers * layout/visual regressions outside approved tolerances * behavioral regressions (click, focus, keyboard nav, validation) * a11y regressions (color contrast, missing labels, keyboard trap, etc.) If in doubt, treat as breaking (major) or provide a compatibility path. ==== 4) Compatibility Paths (Preferred over Majors) ==== Before a major, prefer: * keep old prop, introduce new prop; deprecate old prop * support both old and new values for a release window * adapter/wrapper exports (temporary) to keep old behavior * codemod + migration guide ==== 5) Deprecation Policy ==== * Deprecated APIs remain supported for at least: * 2 minor releases OR 60 days (whichever is longer) within the same major. * Deprecated APIs must include: * JSDoc @deprecated with the replacement * a migration note in CHANGELOG * We support at most the last 2 majors at any time. * Older majors receive only critical security/a11y fixes, then are removed on schedule. ==== 6) Consumer Upgrade Policy ==== * Consumers pin versions (no auto-floating to new majors). * Upgrades happen via explicit dependency bump PRs. * Any component library major upgrade requires consumer-side validation. * Patch/minor upgrades should be safe by default; validation can be localized. ==== 7) Required Evidence for Changes ==== Every change to public components must include at least one: * updated Storybook story (new/changed behavior) * unit/integration test (Testing Library / Playwright component test) * visual regression approval (if visuals changed) ==== 8) Ownership ==== Each component has an owner group responsible for: * reviewing breaking changes * maintaining deprecations/migrations * ensuring test coverage is adequate #### Enforced boundaries (the part that makes this real)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=30 "Edit section: Enforced boundaries (the part that makes this real)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=30 "Edit section's source code: Enforced boundaries (the part that makes this real)")] ##### 1) Hard export boundary via package.json#exports[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=31 "Edit section: 1) Hard export boundary via package.json#exports") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=31 "Edit section's source code: 1) Hard export boundary via package.json#exports")] In packages/ui/package.json: { "name": "@org/ui", "version": "1.12.0", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "./button": { "types": "./dist/button.d.ts", "default": "./dist/button.js" } } } This is the key: if it’s not in exports, it’s not importable (or at least you can make it fail in modern Node/bundlers). ##### 2) Ban deep imports with ESLint[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=32 "Edit section: 2) Ban deep imports with ESLint") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=32 "Edit section's source code: 2) Ban deep imports with ESLint")] In the consumer apps’ ESLint config: module.exports = { rules: { "no-restricted-imports": [ "error", { "patterns": [ { "group": ["@org/ui/''/''", "@org/ui/src/''", "@org/ui/dist/''"], "message": "Deep imports from @org/ui are forbidden. Import only from public entrypoints defined in @org/ui package exports." } ] } ] } }; (Adjust patterns to your repo conventions.) ##### 3) Require a Changeset for any UI public change[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=33 "Edit section: 3) Require a Changeset for any UI public change") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=33 "Edit section's source code: 3) Require a Changeset for any UI public change")] If you use Changesets, you can enforce “no changes to packages/ui without a changeset” in CI. #### CI gates: what to run on every PR[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=34 "Edit section: CI gates: what to run on every PR") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=34 "Edit section's source code: CI gates: what to run on every PR")] ##### The checks you want[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=35 "Edit section: The checks you want") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=35 "Edit section's source code: The checks you want")] 1. Lint + typecheck (affected scope if you’re using Nx/Turborepo; otherwise run for ui + touched apps) 2. Unit/integration tests for @org/ui 3. Build `@org/ui` (so the export boundary is real) 4. Public API diff check (type-level breaking changes) 5. Storybook build + visual regression (Chromatic/Percy) 6. Changeset presence check for UI changes ##### GitHub Actions PR workflow (example)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=36 "Edit section: GitHub Actions PR workflow (example)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=36 "Edit section's source code: GitHub Actions PR workflow (example)")] name: PR Checks on: pull_request: jobs: ui_contract: runs-on: ubuntu-latest steps: * uses: actions/checkout@v4 * uses: pnpm/action-setup@v4 with: version: 9 * uses: actions/setup-node@v4 with: node-version: 20 cache: "pnpm" * run: pnpm install --frozen-lockfile # 1) Enforce no deep imports via lint * name: Lint run: pnpm -r lint # 2) Typecheck (catch breaking prop/type changes early) * name: Typecheck run: pnpm -r typecheck # 3) UI tests * name: UI tests run: pnpm --filter @org/ui test # 4) Build UI (ensures exports/types are generated and valid) * name: Build UI run: pnpm --filter @org/ui build # 5) Require a changeset if UI changed * name: Require changeset for UI changes run: pnpm changeset status --since origin/${{ github.base_ref }} # Optional: add a small script that fails if packages/ui changed but no changeset exists. # 6) Public API report/diff (example using API Extractor) * name: API report (detect breaking API) run: pnpm --filter @org/ui api:check # 7) Storybook + visual regression (Chromatic) * name: Chromatic uses: chromaui/action@v11 with: projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} workingDir: packages/ui exitOnceUploaded: true #### Public API diff gate (TypeScript contract enforcement)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=37 "Edit section: Public API diff gate (TypeScript contract enforcement)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=37 "Edit section's source code: Public API diff gate (TypeScript contract enforcement)")] If you want a strong “did we break the public API?” check, a common approach is Microsoft’s API Extractor (it produces an API report you can diff). In packages/ui: ##### package.json scripts[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=38 "Edit section: package.json scripts") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=38 "Edit section's source code: package.json scripts")] { "scripts": { "build": "tsc -p tsconfig.build.json", "api:check": "api-extractor run --local --verbose" }, "devDependencies": { "@microsoft/api-extractor": "^7.0.0" } } ##### [[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=39 "Edit section: api-extractor.json (minimal)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=39 "Edit section's source code: api-extractor.json (minimal)")] { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", "mainEntryPointFilePath": "/dist/index.d.ts", "apiReport": { "enabled": true, "reportFileName": "ui.api.md" }, "docModel": { "enabled": false }, "dtsRollup": { "enabled": false } } Commit the generated API report (e.g., packages/ui/etc/ui.api.md). Now PRs that change the public API produce a diff that reviewers can treat as “the contract changed.” #### What this buys them (and what it doesn’t)[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=40 "Edit section: What this buys them (and what it doesn’t)") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=40 "Edit section's source code: What this buys them (and what it doesn’t)")] * If they implement exports boundary + no-deep-import lint + semver/changesets + API diff + component-level visual regression, they’ve created an enforceable contract. * They will still sometimes have “large affected surface area” for foundational components, but now: - Breaking changes become explicit and opt-in (major bumps). - Non-breaking changes have strong automated confidence so you don’t default to full manual regression. If you want, I can add the “fail PR if packages/ui/** changed but no changeset exists” script (it’s ~20 lines in Node) and/or show the “two majors supported” policy operationalized (how to remove old majors without drama). ## References[[edit](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&veaction=edit§ion=41 "Edit section: References") | [edit source](https://freemwiki.com/index.php?title=Openai/695d28c7-94c8-8006-836b-96dbe559b5eb&action=edit§ion=41 "Edit section's source code: References")] 1. [↑](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_ref-1 "Jump up")["Monorepo UI Component Versioning"](https://chatgpt.com/share/695d28c7-94c8-8006-836b-96dbe559b5eb). ChatGPT. Retrieved 2026-01-06. 2. [↑](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_ref-2 "Jump up")["changesets-docs.vercel.app"](https://changesets-docs.vercel.app/). changesets-docs.vercel.app. Retrieved 2026-01-06. 3. [↑](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_ref-3 "Jump up")["Nx"](https://nx.dev/docs/features/ci-features/affected). nx.dev. Retrieved 2026-01-06. 4. [↑](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_ref-4 "Jump up")["GitHub"](https://github.com/Tinder/bazel-diff). github.com. Retrieved 2026-01-06. 5. [↑](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_ref-5 "Jump up")["chromatic.com"](https://www.chromatic.com/storybook). chromatic.com. Retrieved 2026-01-06. 6. [↑](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_ref-6 "Jump up")["changesets-docs.vercel.app"](https://changesets-docs.vercel.app/). changesets-docs.vercel.app. Retrieved 2026-01-06. 7. [↑](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_ref-7 "Jump up")["Storybook"](https://storybook.js.org/docs/writing-tests/visual-testing). storybook.js.org. Retrieved 2026-01-06. 8. [↑](https://freemwiki.com/wiki/Openai/695d28c7-94c8-8006-836b-96dbe559b5eb#cite_ref-8 "Jump up")["Nx"](https://nx.dev/docs/features/ci-features/affected). nx.dev. Retrieved 2026-01-06.