← Design System← Design System
Design SystemDesign System19 Th7, 2026Jul 19, 202617 phút đọc13 min read

Versioning & Quản lý ReleaseVersioning & Release Management

Thuộc bộ kiến thức Design System Roadmap.

Tổng quan

Design system không chỉ là một thư viện nội bộ bình thường — nó là dependency được toàn bộ các team sản phẩm trong tổ chức dùng chung cùng một lúc. Khi một service backend thông thường ship một breaking change, chỉ một team chịu ảnh hưởng. Nhưng khi design system ship một breaking change mà không được thông báo trước, hàng chục team có thể bị ảnh hưởng cùng lúc, thường là ngay trên production, thường vào thời điểm tệ nhất (một đợt release freeze, một buổi demo, một ca trực on-call mà không ai am hiểu nội bộ design system). Đây chính là lý do design system cần kỷ luật versioning và release management nghiêm ngặt hơn hầu hết phần mềm khác: bán kính ảnh hưởng (blast radius) của một sai sót là toàn tổ chức, không chỉ một team.

Thực hành versioning và release tốt biến câu chuyện “team design system đổi gì đó và làm hỏng chúng tôi” thành “chúng tôi đã xem changelog, chạy codemod, và upgrade theo lịch của riêng mình.” Các cơ chế giúp điều này khả thi — semantic versioning, chính sách deprecation, changelog, các kênh pre-release, và kế hoạch rollback — là nội dung chính của bài viết này.

Kiến thức nền tảng

Tại sao kỷ luật versioning ở đây quan trọng hơn nơi khác

Ba đặc điểm của design system khiến versioning trở nên đặc biệt rủi ro cao:

Semantic Versioning (SemVer)

Hệ sinh thái design system đã đồng thuận sử dụng Semantic Versioning làm quy ước chuẩn: số phiên bản có dạng MAJOR.MINOR.PATCH (ví dụ 4.3.1), và mỗi phần đều có ý nghĩa hợp đồng (contract) rõ ràng.

PhầnÝ nghĩaVí dụ trong design system
PATCHSửa lỗi tương thích ngược (backwards-compatible), không thay đổi API hay visual-contractSửa lỗi visual khi focus ring của Button bị clip bởi overflow: hidden của phần tử cha. 4.3.0 → 4.3.1
MINORThêm tính năng mới, vẫn tương thích ngượcThêm variant mới size="xl" cho Button, các chỗ dùng cũ không bị ảnh hưởng. 4.3.1 → 4.4.0
MAJORThay đổi không tương thích ngược (backwards-incompatible)Đổi tên prop type của Button thành variant, hoặc đổi default behavior của Modal từ closeOnOverlayClick mặc định true sang false. 4.4.0 → 5.0.0

Giá trị của SemVer là nó cho phép một team consumer đánh giá rủi ro upgrade mà không cần đọc code — một dependency range ^4.3.0 có thể tự động nhận patch và minor release, trong khi major bump đòi hỏi một lần upgrade có chủ đích, được review kỹ. Đây là lý do design system phải nghiêm ngặt về những gì được tính là “breaking”: bất cứ điều gì có thể buộc consumer phải sửa code, sửa markup, sửa visual regression baseline, hay sửa accessibility tree đều được tính là breaking, kể cả khi phần implementation bên dưới “chỉ là CSS.”

Package design token cũng cần kỷ luật tương tự. Đổi tên color.brand.primary thành color.brand.500 là một thay đổi MAJOR đối với package tokens, dù không có logic component nào thay đổi — bất kỳ ai dùng trực tiếp tên token cũ đều sẽ bị vỡ (break).

Phiên bản pre-1.0 và lưu ý về “0.x”

Theo spec của SemVer, 0.y.z dành riêng cho giai đoạn phát triển ban đầu, và mọi thứ có thể thay đổi bất cứ lúc nào — không có cam kết ổn định nào. Nhiều design system chủ động ở lại 0.x trong vài tháng đầu khi API component vẫn đang trong quá trình khám phá, và coi 1.0.0 là thời điểm chính thức cam kết ổn định API. Hãy nêu rõ điều này trong README để những người dùng sớm (early adopter) không nhầm tưởng 0.x có cùng mức bảo đảm như 1.x.

Khái niệm chính

Monorepo và multi-repo cho design system nhiều package

Design system hiếm khi chỉ là một package duy nhất — một thiết lập điển hình gồm design tokens, thư viện component React (hoặc Vue/Web Components), package icon, và đôi khi cả plugin Figma hay trang tài liệu. Vị trí các package này được đặt ở đâu sẽ có hệ quả thực sự đến versioning.

Khía cạnhMonorepo (ví dụ dùng Nx, Turborepo, Lerna/Changesets)Multi-repo (mỗi package một repo riêng)
VersioningCó thể đồng bộ hóa (tokens, react, icons cùng lên 5.0.0) hoặc độc lập theo từng package với tooling chungHoàn toàn độc lập theo repo; không có cơ chế đồng bộ tự động
Thay đổi liên packageDễ dàng gộp một PR cập nhật đồng thời tokens và các component dùng tokens đóCần phối hợp PR qua nhiều repo, dễ bị lệch nhau
CI/CDMột pipeline có thể build, test, publish mọi package nhất quánMỗi repo cần pipeline riêng, nhiều trùng lặp
Khả năng tra cứuMột nơi duy nhất để tìm lịch sử, issue, codeContributor phải biết repo nào sở hữu phần nào
Nhiễu releaseThay đổi icons có thể buộc phải release cả package tokens nếu version bị khóa đồng bộ (trừ khi dùng independent versioning)Consumer chỉ thấy release của package họ dùng
Tooling phổ biếnChangesets là chuẩn de facto cho versioning + sinh changelog trong monorepoTag thủ công hoặc semantic-release theo từng repo

Phần lớn design system đang được bảo trì tích cực (ví dụ Shopify Polaris, Atlaskit của Atlassian) dùng monorepo với versioning độc lập cho từng package — mỗi package có số SemVer riêng, nhưng được build, test, và release từ cùng một repo, dùng công cụ như Changesets để theo dõi package nào thay đổi trong một PR và tự sinh changelog cho từng package. Cách này lấy được phần lớn lợi ích atomicity của monorepo (tokens và components có thể thay đổi cùng nhau) mà không buộc mọi consumer phải bump mọi package trong mỗi lần release.

Chiến lược release: nhịp độ và các kênh

Nhịp độ (cadence). Có hai triết lý chính:

Nhiều team chọn ở giữa: patch release được xuất bản liên tục (việc sửa lỗi không nên bị giữ lại chờ lịch), trong khi minor và major release đi theo nhịp độ được thông báo trước, có thể dự đoán.

Các kênh pre-release. Không phải consumer nào cũng muốn sự ổn định vào cùng một thời điểm — một số team sẵn sàng thử các thay đổi bleeding-edge để đổi lấy quyền tiếp cận sớm hoặc ảnh hưởng đến API cuối cùng. Cơ chế npm dist-tag chuẩn hỗ trợ điều này:

KênhVí dụ npm dist-tagĐối tượng
Stable4.4.0 (latest)Mặc định cho mọi consumer
Release candidate5.0.0-rc.1 (next)Team chuẩn bị upgrade trước khi major GA ra mắt
Beta5.0.0-beta.3 (beta)Team sẵn sàng thử tính năng sắp tới, chấp nhận một số bất ổn
Alpha / canary5.1.0-canary.20260719 (canary)Team design system tự dogfooding, hoặc các team nội bộ ưa mạo hiểm; build từ mọi merge vào main

Xuất bản pre-release cho phép team design system kiểm chứng một breaking change trên code consumer thực tế trước khi nó trở thành latest mặc định, giúp phát hiện các vấn đề mà unit test bên trong repo design system không bao giờ thấy được.

Chiến lược deprecation

Quy tắc quan trọng nhất trong deprecation của design system: không bao giờ xóa bỏ (remove) một thứ trong cùng release mà nó được đánh dấu deprecated. Deprecation và removal là hai sự kiện tách biệt, cách nhau bởi một khoảng thời gian ân hạn (grace period) đủ dài để mọi team consumer di chuyển theo lịch riêng của họ.

Một chuỗi deprecation lành mạnh trông như sau:

  1. Thông báo & cảnh báo (MINOR release). API cũ vẫn hoạt động y như trước, nhưng việc sử dụng nó giờ sẽ kích hoạt một cảnh báo — console warning lúc runtime (ví dụ: prop type của Button đã deprecated, dùng variant thay thế), tag JSDoc @deprecated trong TypeScript mà linter/IDE hiển thị, và một mục trong changelog.
  2. Thời gian ân hạn (grace period). Một khoảng thời gian được nêu rõ (thường là một đến hai chu kỳ minor/major release, hoặc một số tháng cố định) trong đó cả API cũ và mới cùng hoạt động song song. Team design system nên theo dõi số liệu sử dụng nội bộ (usage analytics) nếu có thể để biết bao nhiêu consumer đã migrate.
  3. Cung cấp lộ trình migration. Một hướng dẫn migration bằng văn bản mô tả chính xác thay đổi gì và tại sao. Với các thay đổi mang tính cơ học thuần túy (đổi tên prop, đổi đường dẫn import), hãy cung cấp một codemod — một script (thường xây trên jscodeshift) tự động viết lại code của consumer, biến việc phải mất hàng giờ tìm-và-thay-thế thủ công thành một lệnh duy nhất như npx @acme/design-system-codemods rename-button-prop.
  4. Xóa bỏ (MAJOR release). Chỉ khi grace period đã kết thúc — và lý tưởng là khi telemetry sử dụng cho thấy API deprecated gần như không còn ai dùng — thì major version tiếp theo mới thực sự xóa API cũ. Việc xóa bỏ này bản thân nó cũng là một breaking change và phải được ghi rõ trong changelog.

Chính trình tự này giúp design system tiến hóa API mà không bao giờ buộc 40 team sản phẩm phải migrate khẩn cấp trong cùng một ngày.

Changelog

Changelog là bề mặt giao tiếp chính giữa team design system và mọi consumer, và nó xứng đáng được chăm chút không kém gì bản thân code. Định dạng Keep a Changelog là quy ước được áp dụng rộng rãi:

Một mục changelog tốt trả lời ba câu hỏi, không chỉ một:

Ví dụ một mục changelog breaking-change tốt:

## [5.0.0] - 2026-08-04

### Changed
- **BREAKING:** Prop `type` của `Button` đã được đổi tên thành `variant` để
  tránh xung đột với attribute HTML gốc `type` (`button`/`submit`/`reset`).
  Chạy `npx @acme/design-system-codemods rename-button-prop` để cập nhật
  tự động, hoặc xem [hướng dẫn migration](./MIGRATIONS.md#v5).

  ```diff
  - <Button type="primary">Save</Button>
  + <Button variant="primary">Save</Button>

Deprecated


### Roadmap & milestone

Changelog thông báo những gì đã xảy ra; **roadmap công khai** thông báo những gì sắp tới. Các team consumer nhìn thấy "v6.0.0, dự kiến Q1 2027, sẽ xóa prop `Modal.onClose` đã deprecated và ngừng hỗ trợ React 16" có thể lên kế hoạch sprint của riêng họ thay vì bị bất ngờ bởi một major release. Roadmap thường tồn tại dưới dạng một GitHub Project board được ghim, một file `ROADMAP.md`, hoặc một mục trong trang tài liệu của design system, và được xem xét trong cùng diễn đàn với các quyết định [Governance & Contribution](/notes/design-system/11-governance-and-contribution), vì quyết định điều gì sẽ breaking tiếp theo về bản chất là một câu hỏi governance.

### Chiến lược rollback

Dù có mọi biện pháp bảo vệ, cuối cùng vẫn sẽ có một release ship một regression chỉ lộ ra dưới lưu lượng production thực tế. Design system cần một lộ trình rollback nhanh chóng, đã được diễn tập kỹ:

- **Pin-back luôn khả thi.** Vì mỗi release là một số phiên bản SemVer đã được xuất bản, bất biến (immutable), bất kỳ consumer nào cũng có thể revert ngay lập tức bằng cách ghim `package.json` về phiên bản known-good cuối cùng và cài lại — không cần hành động gì từ phía team design system. Đây là lý do việc xuất bản mọi release (kể cả patch) như một số phiên bản rời rạc, vĩnh viễn lại quan trọng: đó chính là cơ chế rollback.
- **Deprecate/unpublish phiên bản lỗi tập trung.** Với một regression nghiêm trọng (ví dụ lỗi accessibility hay lỗ hổng bảo mật), team design system có thể đánh dấu phiên bản lỗi là deprecated trên registry (`npm deprecate @acme/react-components@4.5.0 "Chứa regression nghiêm trọng, hãy upgrade lên 4.5.1"`), việc này sẽ hiện cảnh báo cho bất kỳ ai cài phiên bản đó, mà không phá vỡ lockfile hiện có.
- **Ship một patch ngay trong ngày.** Cách sửa nhanh nhất thường là một patch release mới hoàn tác thay đổi gây lỗi, xuất bản trong vòng một giờ, để những consumer đã upgrade lên phiên bản lỗi có thể tiến lên bản vá thay vì phải lùi lại.
- **Feature-flag các thay đổi rủi ro.** Với các thay đổi hành vi lớn hơn, đặt chúng sau một prop hoặc flag opt-in cho phép design system ship code trong một release mà không đổi hành vi mặc định, tách biệt "code đã tồn tại" khỏi "code đang hoạt động," giúp cả roll-forward lẫn rollback đều rẻ hơn.

Khả năng rollback nên được kiểm thử, không phải chỉ giả định — cũng giống như [CI/CD pipeline](/notes/devops/11-ci-cd) cho application code diễn tập rollback, pipeline release của design system cũng nên có một runbook "làm sao để un-ship cái này" được ghi lại và luyện tập.

### Bảng tổng quan: loại thay đổi → SemVer bump → giao tiếp cần thiết

| Loại thay đổi | SemVer bump | Giao tiếp cần thiết |
|---|---|---|
| Bug fix, không thay đổi API/visual-contract (ví dụ sửa lỗi focus-ring bị clip của `Button`) | PATCH | Mục changelog dưới `Fixed`; không cần bước migration |
| Tính năng mới, tương thích ngược (ví dụ thêm `size="xl"` cho `Button`) | MINOR | Mục changelog dưới `Added`; có thể cập nhật docs, không cần migration |
| Deprecation một API hiện có, API cũ vẫn hoạt động (ví dụ `Modal.onClose` bị deprecated, thay bằng `onOpenChange`) | MINOR | Mục changelog dưới `Deprecated`; cảnh báo runtime; hướng dẫn migration được công bố; nêu rõ ngày sẽ xóa |
| Breaking change: đổi tên/xóa prop, đổi default behavior, xóa component (ví dụ `Button.type` đổi tên thành `variant`) | MAJOR | Mục changelog dưới `Changed`/`Removed` đánh dấu `BREAKING`; hướng dẫn migration; codemod nếu là thay đổi cơ học; thông báo roadmap trước; khuyến nghị kiểm chứng qua pre-release (`rc`) |

## Best Practices

Coi changelog là một sản phẩm bàn giao (deliverable) quan trọng ngang với chính thay đổi code — một PR làm thay đổi hành vi component mà không có mục changelog nên bị fail khi review, giống như khi thiếu test vậy. Tự động hóa việc bump version và sinh changelog bất cứ khi nào có thể (Changesets, `semantic-release`) để quy trình không phụ thuộc vào việc ai đó nhớ làm đúng dưới áp lực release. Gộp các breaking change liên quan vào một major release duy nhất thay vì rải rác nhiều major liên tiếp — mỗi lần bump major version đều tốn công sức migration của mọi team consumer, nên hãy gộp lại để giảm thiểu số lần phải trả cái giá đó. Luôn ship một hướng dẫn migration và, với các thay đổi mang tính cơ học, một codemod đi kèm ngay trong major release — thông báo một breaking change mà không có lộ trình khắc phục cụ thể chỉ biến thông báo đó thành sự lo lắng. Theo dõi mức độ sử dụng thực tế của API đã deprecated (qua lint rule, telemetry, hoặc grep qua các repo nội bộ) trước khi xóa bất cứ thứ gì, vì "chúng tôi đã thông báo ba tháng trước" không đồng nghĩa với "không còn ai dùng nữa." Xuất bản một roadmap được cập nhật liên tục và xem xét lại nó trong cùng diễn đàn governance được mô tả ở [Governance & Contribution](/notes/design-system/11-governance-and-contribution), vì lập kế hoạch release là một hoạt động governance, không chỉ đơn thuần là kỹ thuật. Cuối cùng, đảm bảo thông báo release đến được với consumer qua các kênh họ thực sự theo dõi — một changelog không ai đọc thì không phải là giao tiếp; hãy kết hợp nó với các thực hành tiếp cận được nêu ở [Adoption, Community & Support](/notes/design-system/13-adoption-community-and-support).

## Tài liệu tham khảo

- [Semantic Versioning 2.0.0](https://semver.org/)
- [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
- [Changesets — versioning cho monorepo](https://github.com/changesets/changesets)
- [npm — About semantic versioning](https://docs.npmjs.com/about-semantic-versioning)
- [jscodeshift — toolkit chạy codemod](https://github.com/facebook/jscodeshift)
- [semantic-release — quản lý version tự động](https://semantic-release.gitbook.io/semantic-release/)
- [roadmap.sh — Design System](https://roadmap.sh/design-system)

Part of the Design System Roadmap knowledge base.

Overview

A design system is not just another internal library — it is a dependency shared by every product team in the organization at once. When a typical backend service ships a breaking change, one team feels the pain. When a design system ships an uncommunicated breaking change, dozens of teams can feel it simultaneously, often in production, often at the worst possible time (a release freeze, a demo, an on-call rotation with nobody familiar with the design system’s internals). This is the central reason design systems need more disciplined versioning and release management than most software: the blast radius of a mistake is organization-wide, not team-wide.

Good versioning and release practice turns “the design system team changed something and broke us” into “we saw the changelog, ran the codemod, and upgraded on our own schedule.” The mechanisms that make this possible — semantic versioning, deprecation policies, changelogs, pre-release channels, and rollback plans — are the subject of this note.

Fundamentals

Why versioning discipline matters more here than elsewhere

Three properties of a design system make versioning unusually high-stakes:

Semantic Versioning (SemVer)

The design system ecosystem has converged on Semantic Versioning as the standard scheme: version numbers look like MAJOR.MINOR.PATCH (e.g. 4.3.1), and each segment has a precise contractual meaning.

SegmentMeaningDesign-system example
PATCHBackwards-compatible bug fix, no API or visual-contract changeFixed a visual bug where Button’s focus ring was clipped by overflow: hidden on its parent. 4.3.0 → 4.3.1
MINORBackwards-compatible new functionalityAdded a new size="xl" variant to Button, existing usages unaffected. 4.3.1 → 4.4.0
MAJORBackwards-incompatible changeRenamed the Button prop type to variant, or changed Modal’s default closeOnOverlayClick from true to false. 4.4.0 → 5.0.0

The value of SemVer is that it lets a consuming team reason about upgrade risk without reading the code — a ^4.3.0 dependency range can absorb patch and minor releases automatically, while a major bump requires a deliberate, reviewed upgrade. This is why a design system must be strict about what counts as “breaking”: anything that could require a consumer to change their code, their markup, their visual regression baselines, or their accessibility tree counts as breaking, even if the underlying implementation is “just CSS.”

A design token package deserves the same discipline. Renaming color.brand.primary to color.brand.500 is a MAJOR change for the tokens package, even though no component logic changed — anyone consuming the raw token name breaks.

Pre-1.0 versions and the “0.x” caveat

Per the SemVer spec, 0.y.z is explicitly for initial development, and anything may change at any time — there is no promised stability. Many design systems intentionally stay in 0.x during their first few months while the component API is still being discovered, and treat 1.0.0 as the moment they commit to API stability. Communicate this clearly in the README so early adopters don’t assume 0.x carries the same guarantees as 1.x.

Key Concepts

Monorepo vs. multi-repo for a multi-package design system

A design system is rarely one package — a typical setup has design tokens, a React (or Vue/Web Components) component library, an icon package, and sometimes a Figma-plugin or documentation site. Where these packages live has real versioning consequences.

AspectMonorepo (e.g. via Nx, Turborepo, Lerna/Changesets)Multi-repo (separate repos per package)
VersioningCan be unified (tokens, react, icons all move to 5.0.0 together) or independent per package with shared toolingFully independent per repo; no automatic coordination
Cross-package changesEasy to land an atomic PR that updates tokens and the components that consume them togetherRequires coordinated PRs across repos, easy to get out of sync
CI/CDOne pipeline can build, test, and publish all packages consistentlyEach repo needs its own pipeline, more duplication
DiscoverabilityOne place to search history, issues, and codeContributors must know which repo owns what
Release noiseA change to icons can force a release of the tokens package too if versions are locked in lockstep (unless using independent versioning)Consumers only see releases for packages they use
Common toolingChangesets is the de facto standard for versioning + changelog generation in a monorepoManual tagging or per-repo semantic-release

Most actively maintained design systems (e.g. Shopify Polaris, Atlassian’s Atlaskit) use a monorepo with independent per-package versioning — each package gets its own SemVer number, but they are built, tested, and released from the same repository, using tools like Changesets to track which packages changed in a given PR and to auto-generate a changelog entry per package. This gets most of the monorepo’s atomicity benefits (tokens and components can change together) without forcing every consumer to bump every package on every release.

Release strategy: cadence and channels

Cadence. There are two broad philosophies:

Many teams land in the middle: patch releases go out continuously (bug fixes should never be held hostage to a schedule), while minor and major releases follow a predictable, announced cadence.

Pre-release channels. Not every consumer wants stability at the same moment — some teams are happy to try bleeding-edge changes in exchange for early access or influence over the final API. Standard npm dist-tags support this:

Channelnpm dist-tag exampleAudience
Stable4.4.0 (latest)Default for all consumers
Release candidate5.0.0-rc.1 (next)Teams preparing to upgrade before a major GA
Beta5.0.0-beta.3 (beta)Teams willing to test upcoming features, expect some churn
Alpha / canary5.1.0-canary.20260719 (canary)Design system team’s own dogfooding, or adventurous internal teams; built from every merge to main

Publishing pre-releases lets the design system team validate a breaking change against real consumer code before it becomes the default latest, which catches problems that unit tests inside the design system repo would never see.

Deprecation strategy

The single most important rule of design-system deprecation: never remove something in the same release in which you deprecate it. Deprecation and removal are two separate events, separated by a grace period long enough for every consuming team to migrate on their own schedule.

A healthy deprecation sequence looks like this:

  1. Announce & warn (MINOR release). The old API keeps working exactly as before, but using it now triggers a warning — a console warning at runtime (e.g. Button’s type prop is deprecated, use variant instead), a TypeScript @deprecated JSDoc tag that lints/IDEs surface, and a changelog entry.
  2. Grace period. A stated window (commonly one to two minor/major release cycles, or a fixed number of months) during which both the old and new API work side by side. The design system team tracks internal usage analytics where possible to see how many consumers have migrated.
  3. Provide a migration path. A written migration guide describing exactly what changed and why. For purely mechanical renames (a prop rename, an import path change), ship a codemod — a script (often built on jscodeshift) that rewrites consumer code automatically, turning what would be hours of manual find-and-replace into a single command like npx @acme/design-system-codemods rename-button-prop.
  4. Remove (MAJOR release). Only once the grace period has elapsed — and ideally once usage telemetry shows the deprecated API is close to unused — does the next major version actually delete the old API. This removal is itself a breaking change and is documented as such in the changelog.

This sequencing is what allows a design system to evolve its API without ever forcing an emergency same-day migration on 40 product teams.

Changelogs

A changelog is the primary communication surface between the design system team and every consumer, and it deserves as much care as the code itself. The Keep a Changelog format is the widely adopted convention:

A good individual changelog entry answers three questions, not just one:

Example of a strong breaking-change entry:

## [5.0.0] - 2026-08-04

### Changed
- **BREAKING:** `Button`'s `type` prop has been renamed to `variant` to avoid
  colliding with the native HTML `type` attribute (`button`/`submit`/`reset`).
  Run `npx @acme/design-system-codemods rename-button-prop` to update
  automatically, or see the [migration guide](./MIGRATIONS.md#v5).

  ```diff
  - <Button type="primary">Save</Button>
  + <Button variant="primary">Save</Button>

Deprecated


### Roadmap & milestones

Changelogs communicate what already happened; a **public roadmap** communicates what is coming. Consuming teams that can see "v6.0.0, planned for Q1 2027, will remove the deprecated `Modal.onClose` prop and drop support for React 16" can plan their own sprints around it instead of being ambushed by a major release. A roadmap typically lives as a pinned GitHub Project board, a `ROADMAP.md`, or a section of the design system's documentation site, and is reviewed in the same forum as [governance and contribution](/notes/design-system/11-governance-and-contribution) decisions, since deciding what breaks next is fundamentally a governance question.

### Rollback strategy

Despite every safeguard, a release will eventually ship a regression that only shows up under real production traffic. A design system needs a fast, well-rehearsed rollback path:

- **Pin-back is always possible.** Because every release is an immutable, published SemVer version, any consumer can revert instantly by pinning `package.json` to the last known-good version and re-installing — no design-system-side action required. This is why publishing every release (even patches) as a discrete, permanent version number matters: it's the rollback mechanism.
- **Deprecate/unpublish the bad version centrally.** For a severe regression (e.g. an accessibility failure or a security issue), the design system team can mark the broken version as deprecated on the registry (`npm deprecate @acme/react-components@4.5.0 "Contains a critical regression, upgrade to 4.5.1"`), which surfaces a warning to anyone installing it, without breaking existing lockfiles.
- **Ship a same-day patch.** The fastest real fix is usually a new patch release that reverts the offending change, published within the hour, so that consumers who already upgraded to the broken version can move forward to the fix rather than backward.
- **Feature-flag risky changes.** For larger behavioral changes, gating them behind an opt-in prop or flag lets the design system ship the code in a release without changing default behavior, decoupling "the code exists" from "the code is active," which makes both roll-forward and rollback cheaper.

Rollback capability should be tested, not assumed — the same way [CI/CD pipelines](/notes/devops/11-ci-cd) for application code rehearse rollback, a design system's release pipeline should have a documented, practiced "how do we un-ship this" runbook.

### Change type to SemVer bump, at a glance

| Change type | SemVer bump | Communication needed |
|---|---|---|
| Bug fix, no API/visual-contract change (e.g. fixed `Button` focus-ring clipping) | PATCH | Changelog entry under `Fixed`; no migration steps |
| New backwards-compatible feature (e.g. added `size="xl"` to `Button`) | MINOR | Changelog entry under `Added`; optional docs update, no migration required |
| Deprecation of an existing API, old API still works (e.g. `Modal.onClose` deprecated in favor of `onOpenChange`) | MINOR | Changelog entry under `Deprecated`; runtime warning; migration guide published; removal date stated |
| Breaking change: renamed/removed prop, changed default behavior, removed component (e.g. `Button.type` renamed to `variant`) | MAJOR | Changelog entry under `Changed`/`Removed` marked `BREAKING`; migration guide; codemod if mechanical; roadmap notice ahead of time; pre-release (`rc`) validation recommended |

## Best Practices

Treat the changelog as a deliverable of equal importance to the code change itself — a PR that changes component behavior without a changelog entry should fail review, the same way it would fail without tests. Automate version bumping and changelog generation wherever possible (Changesets, `semantic-release`) so the process doesn't depend on someone remembering to do it correctly under release pressure. Bundle related breaking changes into a single major release rather than trickling out several majors in quick succession — every major version bump costs every consuming team migration effort, so batch to minimize how often that cost is paid. Always ship a migration guide and, for mechanical changes, a codemod alongside the major release itself — announcing a breaking change without a concrete path to fix it just converts the announcement into anxiety. Track real deprecated-API usage (via lint rules, telemetry, or a grep across internal repos) before removing anything, since "we announced it three months ago" is not the same as "no one is using it anymore." Publish a rolling roadmap and revisit it in the same governance forum described in [Governance & Contribution](/notes/design-system/11-governance-and-contribution), because release planning is a governance activity, not just an engineering one. Finally, make sure release announcements reach consumers through the channels they actually watch — a changelog nobody reads is not communication; pair it with the outreach practices covered in [Adoption, Community & Support](/notes/design-system/13-adoption-community-and-support).

## References

- [Semantic Versioning 2.0.0](https://semver.org/)
- [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
- [Changesets — versioning for monorepos](https://github.com/changesets/changesets)
- [npm — About semantic versioning](https://docs.npmjs.com/about-semantic-versioning)
- [jscodeshift — toolkit for running codemods](https://github.com/facebook/jscodeshift)
- [semantic-release — automated version management](https://semantic-release.gitbook.io/semantic-release/)
- [roadmap.sh — Design System](https://roadmap.sh/design-system)