Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 37 — Versioning a Design System

UI Design and Design Handoff Bài 37/60

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn là designer trong một startup fintech ở Sài Gòn. Design system của bạn đã có hơn 40 component, được 3 team sản phẩm khác nhau dùng chung. Một sáng đẹp trời, bạn "cải tiến" component Button — đổi tên prop type thành variant, bỏ luôn size xs vì thấy ít ai dùng. Bạn publish library, cảm thấy rất hài lòng.

Đến trưa, ba nhóm developer nhắn tin dồn dập: màn hình thanh toán vỡ layout, nút "Xác nhận" biến mất, có team còn không build được app. Lý do? Bạn vừa tạo ra một breaking change mà không báo trước, không đánh số phiên bản, không có cách nào để họ "đóng băng" ở phiên bản cũ trong khi vá lỗi.

Đây chính xác là vấn đề mà versioning (đánh phiên bản) một design system giải quyết. Khi design system chỉ phục vụ một người, bạn muốn đổi gì cũng được. Nhưng ngay khi nó trở thành sản phẩm dùng chung — có nhiều "khách hàng" là các team khác — nó cần một hợp đồng rõ ràng: "Nếu tôi thay đổi thứ này, bạn có thể yên tâm rằng nó sẽ/sẽ không làm hỏng công việc của bạn." Versioning chính là ngôn ngữ của hợp đồng đó.

Bài này dạy bạn cách đánh số phiên bản một cách chuyên nghiệp: hiểu và áp dụng SemVer, phân biệt các loại thay đổi, quản lý changelog, xử lý deprecation, và phối hợp giữa file Figma với package code. Đây là kỹ năng biến bạn từ một người "làm UI kit đẹp" thành một người vận hành design system như một sản phẩm thật sự.

Khái niệm cốt lõi

SemVer — Semantic Versioning cho design system

SemVer là quy ước đánh số phiên bản phổ biến nhất trong ngành phần mềm, và nó áp dụng gần như hoàn hảo cho design system. Số phiên bản có dạng ba phần:

MAJOR.MINOR.PATCH
   2  .  5  .  3

Ý nghĩa của từng phần, đọc từ trái sang phải theo mức độ "nghiêm trọng" của thay đổi:

  • MAJOR (số đầu) — tăng khi có breaking change: thay đổi khiến code hoặc thiết kế đang dùng phiên bản cũ sẽ hỏng nếu nâng cấp. Ví dụ: xóa một component, đổi tên prop, đổi tên token, thay đổi hành vi mặc định.
  • MINOR (số giữa) — tăng khi thêm tính năng mới tương thích ngược: thêm component mới, thêm variant mới, thêm prop mới có giá trị mặc định. Người dùng cũ không cần sửa gì cả.
  • PATCH (số cuối) — tăng khi sửa lỗi mà không đổi API: sửa sai màu, chỉnh lại padding lệch 2px, vá bug accessibility. Không thêm, không xóa gì.
Một cách ghi nhớ đơn giản mình hay dạy học viên:

2.0.0  — Breaking: đổi tên/xóa component, đổi API prop        → "Coi chừng, phải sửa code"
1.5.0  — New:      thêm component/variant/prop mới             → "Cứ nâng cấp, có thêm đồ chơi"
1.4.7  — Fix:      sửa màu sai, vá bug spacing, sửa a11y       → "Nâng cấp đi, chỉ tốt hơn thôi"

Thế nào là "breaking change" trong design system?

Với code thuần túy, breaking change khá rõ. Nhưng design system có hai mặt — thiết kế (Figma)code (component) — nên định nghĩa breaking rộng hơn. Một thay đổi được coi là breaking nếu nó khiến:

  • Developer phải sửa code khi nâng cấp (đổi tên prop, xóa prop, đổi kiểu dữ liệu).
  • Giao diện đang chạy bị thay đổi ngoài ý muốn (đổi màu primary, đổi chiều cao mặc định của button làm vỡ layout).
  • Designer phải cập nhật lại file thiết kế thủ công (đổi tên component trong library làm mất liên kết instance, đổi tên variable/token).
Ví dụ tinh tế: bạn tăng border-radius mặc định của Card từ 8px lên 12px vì thấy "đẹp hơn". Nghe như patch, nhưng nếu nhiều màn hình đã canh chỉnh layout dựa trên bo góc 8px, thì đây là thay đổi hình ảnh có thể coi là breaking. Nguyên tắc vàng: khi nghi ngờ, hãy coi là nghiêm trọng hơn. Đánh MINOR nhầm thành MAJOR chỉ gây phiền nhẹ; đánh MAJOR nhầm thành PATCH có thể làm vỡ sản phẩm của người khác.

Deprecation — con đường "về hưu" tử tế cho component

Bạn không nên xóa thẳng một component đang được dùng. Thay vào đó, bạn deprecate (đánh dấu ngừng khuyến nghị) nó trước, cho mọi người thời gian chuyển đổi, rồi mới xóa ở một MAJOR sau. Quy trình chuẩn gồm ba giai đoạn:

  • Deprecate ở một MINOR — component vẫn hoạt động, nhưng thêm cảnh báo (trong Figma đặt prefix [Deprecated] hoặc emoji cảnh báo trong tên; trong code thêm console warning). Cung cấp rõ "dùng cái gì thay thế".
  • Duy trì song song — component cũ và mới cùng tồn tại một khoảng thời gian (thường 1–2 quý), đủ để các team chuyển dần.
  • Xóa ở MAJOR tiếp theo — kèm changelog và migration guide rõ ràng.

Changelog — cuốn nhật ký thay đổi

Mỗi phiên bản cần một dòng ghi chú: có gì mới, sửa gì, và đặc biệt là thay đổi nào bắt buộc phải chú ý. Changelog tốt viết cho con người đọc, nhóm theo Added / Changed / Fixed / Deprecated / Removed, và nêu rõ cách migrate nếu có breaking change. Đây là thứ team dev sẽ đọc đầu tiên trước khi quyết định nâng cấp.

Tình huống thực tế

Ví dụ 1 — Startup fintech "MoMo-style" và bài học đổi tên token

Một công ty ví điện tử ở Việt Nam (gọi là VíPay cho dễ hình dung) có design system phục vụ 5 team: app khách hàng, app merchant, web admin, landing page và một mini-app trong siêu ứng dụng đối tác. Design lead quyết định "dọn dẹp" tên token màu: đổi color-brand-pink thành color-primary cho chuẩn.

Nghe hợp lý về mặt đặt tên. Nhưng đây là breaking change kinh điển: mọi nơi tham chiếu color-brand-pink trong code đều gãy. Team quyết định nâng version từ 3.4.2 lên 4.0.0, và quan trọng hơn — họ không xóa token cũ ngay. Ở 4.0.0, họ tạo color-primary là token chính, đồng thời giữ color-brand-pink như một alias trỏ về color-primary và đánh dấu deprecated. Changelog ghi rõ: "token color-brand-pink sẽ bị xóa ở 5.0.0, hãy chuyển sang color-primary trước Q3."

Bài học: một MAJOR được lên kế hoạch tốt vẫn cho phép "hạ cánh mềm". Nhờ giữ alias, không team nào bị vỡ app ngay trong ngày release, và họ có nguyên một quý để chuyển đổi. Con số thực tế: 5 team hoàn tất migrate trong 6 tuần, không có incident nào ở production.

Ví dụ 2 — Team e-commerce và cái bẫy "chỉ sửa nhẹ thôi mà"

Một sàn thương mại điện tử Đông Nam Á (kiểu Tiki/Shopee) có junior designer sửa component PriceTag. Bạn ấy thấy giá tiền hiển thị hơi to, nên giảm font-size mặc định từ 16px xuống 14px và publish như một patch (2.1.42.1.5) vì nghĩ "chỉ chỉnh xíu font".

Kết quả: trên trang danh sách sản phẩm với hàng nghìn item, giá tiền nhỏ lại khiến tỷ lệ click giảm rõ rệt trong A/B test tự nhiên (team growth phát hiện conversion giảm ~3% trong hai ngày). Vấn đề không nằm ở font-size, mà ở chỗ phân loại sai mức độ thay đổi. Đây là thay đổi hình ảnh ảnh hưởng đến hành vi người dùng — đáng lẽ phải là MINOR có thông báo, hoặc thậm chí bàn bạc trước, chứ không phải một patch âm thầm.

Bài học: PATCH nghĩa là "sửa cho đúng ý định ban đầu", không phải "đổi ý định". Nếu font 16px là chủ đích thiết kế ban đầu và bạn muốn đổi sang 14px, đó là một quyết định thiết kế mới — cần MINOR (thêm chọn lựa) hoặc MAJOR (đổi mặc định), kèm changelog để team biết mà theo dõi.

Ví dụ 3 — Agency và việc đồng bộ version giữa Figma và code

Một agency ở Hà Nội xây design system cho khách hàng, phát hành đồng thời file Figma library và một npm package. Ban đầu họ để hai thứ chạy version riêng: Figma library ghi "v12" (số nội bộ tự tăng của Figma), còn npm package ở 2.3.0. Developer và designer liên tục hiểu lầm nhau: "Bạn đang dùng v mấy?" — "v12" — "Nhưng code tôi là 2.3, không khớp gì cả!"

Họ sửa bằng cách đồng bộ số phiên bản có ý nghĩa giữa hai bên: mỗi khi release, họ đặt tên trang cover trong Figma là Design System — v2.3.0, khớp đúng với npm package. Figma vẫn tự lưu version history riêng, nhưng "số phiên bản chính thức" mà cả hai bên nói chuyện với nhau là một con số SemVer duy nhất. Kèm theo đó là một trang "Release Notes" ngay trong file Figma phản chiếu changelog của code.

Bài học: thiết kế và code là hai mặt của cùng một sản phẩm, nên chúng cần một ngôn ngữ version chung. Số version nội bộ tự động của Figma (v1, v2, v3...) không thay thế được SemVer — nó chỉ là lịch sử lưu file, không mang ý nghĩa breaking/feature/fix.

Hướng dẫn từng bước

Đây là quy trình versioning bạn có thể áp dụng ngay cho design system của mình.

Bước 1 — Chọn version khởi điểm. Nếu design system còn đang thử nghiệm, chưa ổn định, hãy để version 0.y.z (ví dụ 0.8.0). Quy ước SemVer cho phép ở giai đoạn 0.x mọi thứ có thể breaking bất cứ lúc nào — đây là "vùng an toàn để thử". Khi bạn tự tin nó đã ổn định và có người thật sự phụ thuộc vào, hãy phát hành 1.0.0. Đó là lời cam kết: "Từ giờ tôi sẽ tôn trọng SemVer nghiêm túc."

Bước 2 — Với mỗi thay đổi, phân loại trước khi làm. Trước khi sửa bất cứ gì, tự hỏi: thay đổi này khiến người dùng phải sửa code/thiết kế của họ không? Nếu có → MAJOR. Chỉ thêm cái mới, không đụng cái cũ? → MINOR. Chỉ vá lỗi, giữ nguyên API và ý định thiết kế? → PATCH.

Bước 3 — Gom nhóm thay đổi thành release, đừng bump lung tung. Đừng release mỗi lần sửa một dòng. Gom các thay đổi liên quan lại thành một đợt phát hành có chủ đích — ví dụ mỗi hai tuần một lần, hoặc mỗi khi hoàn thành một nhóm tính năng. Điều này giúp team dev nâng cấp theo nhịp, không phải chạy theo bạn hằng ngày.

Bước 4 — Viết changelog cho mỗi release. Theo cấu trúc chuẩn:

[2.0.0] — 2026-06-27

Removed (BREAKING)

  • Bỏ component LegacyModal. Dùng Dialog thay thế. Xem migration guide.

Changed (BREAKING)

  • Đổi prop Button.type thành Button.variant.

Added

  • Thêm component Toast với 4 variant (info/success/warning/error).

Fixed

  • Sửa contrast của text placeholder cho đạt WCAG AA.

Deprecated

  • color-brand-pink sẽ bị xóa ở 3.0.0. Chuyển sang color-primary.

Bước 5 — Xử lý deprecation trước, xóa sau. Không bao giờ xóa và đổi tên trong cùng một release đối với thứ đang được dùng nhiều. Deprecate ở MINOR, giữ song song, xóa ở MAJOR.

Bước 6 — Đồng bộ Figma và code. Đặt số SemVer lên trang cover của file Figma, tạo trang Release Notes phản chiếu changelog, và khi tag version cho package code, dùng đúng con số đó. Một số team gắn link file Figma vào chính release note của package để hai bên luôn trỏ về nhau.

Bước 7 — Thông báo. Release không có nghĩa lý gì nếu không ai biết. Gửi một tin nhắn ngắn gọn vào kênh chung (Slack/Telegram): version mới, điểm nổi bật, có breaking change không, link changelog. Với MAJOR, thông báo trước cả tuần.

Lỗi thường gặp & mẹo

Lỗi 1 — Dùng version tùy hứng. Nhảy từ 1.0 lên 2.0 chỉ vì "cảm giác đây là bản lớn", trong khi thực chất chỉ thêm vài component. Version phải phản ánh bản chất kỹ thuật của thay đổi, không phải cảm xúc về độ "hoành tráng".

Lỗi 2 — Bump PATCH cho thay đổi hình ảnh có tác động. Như ví dụ PriceTag ở trên. Mẹo: nếu thay đổi khiến ai đó nhìn màn hình và nhận ra "ơ khác rồi", đó không còn là patch âm thầm.

Lỗi 3 — Xóa thẳng không deprecate. Đây là cách nhanh nhất để mất niềm tin của team dev. Luôn cho đường lui.

Lỗi 4 — Changelog viết cho máy, không viết cho người. "Update Button component" là changelog vô dụng. Phải nói cái gì đổi, tại sao, và tôi cần làm gì.

Lỗi 5 — Bỏ qua giai đoạn 0.x. Nhiều team vội vàng gắn 1.0.0 khi hệ thống còn thay đổi xoành xoạch, rồi buộc phải bump MAJOR liên tục, gây mệt mỏi. Cứ thoải mái ở 0.x cho đến khi thật sự ổn định.

Mẹo hay: Với breaking change lớn, hãy cân nhắc phát hành song song hai phiên bản trong một khoảng thời gian (v1 và v2 cùng tồn tại), giống cách các thư viện lớn làm. Trong Figma, bạn có thể giữ hai library riêng biệt cho đến khi mọi team chuyển xong.

Mẹo về nhịp release: đặt lịch cố định (ví dụ release vào thứ Ba mỗi hai tuần) giúp cả tổ chức có kỳ vọng ổn định và giảm hoảng loạn khi có thay đổi.

Bài tập thực hành

  • Phân loại thay đổi. Với design system giả định đang ở 2.4.1, hãy xác định version tiếp theo cho từng thay đổi sau và giải thích ngắn gọn:
- (a) Sửa màu icon lỗi từ đỏ nhạt sang đỏ đạt WCAG AA. - (b) Thêm variant ghost cho Button. - (c) Đổi tên token spacing-md thành space-3. - (d) Bỏ component Accordion cũ. - (e) Thêm prop loading (mặc định false) cho Button.

  • Viết changelog. Gom các thay đổi ở bài 1 thành một release note hoàn chỉnh theo cấu trúc Added / Changed / Fixed / Deprecated / Removed, xác định đúng số version cuối cùng của cả đợt.
  • Lập kế hoạch deprecation. Bạn muốn thay OldCard bằng Card mới. Viết ra kế hoạch 3 giai đoạn: deprecate ở version nào, giữ song song bao lâu, xóa ở version nào — kèm nội dung cảnh báo bạn sẽ đặt trong tên component Figma.
  • Đồng bộ Figma–code. Mô tả cách bạn sẽ hiển thị số version trên file Figma của mình sao cho khớp với package code, và bạn sẽ đặt trang Release Notes ở đâu.
Gợi ý đáp án bài 1: (a) PATCH → 2.4.2; (b) MINOR → 2.5.0; (c) MAJOR → 3.0.0; (d) MAJOR → 3.0.0; (e) MINOR. Khi gom cả đợt, thay đổi nghiêm trọng nhất quyết định số version — có breaking (c, d) nên cả release là 3.0.0.

Tóm tắt

Versioning là thứ biến design system từ "một file Figma đẹp" thành "một sản phẩm đáng tin cậy để nhiều team phụ thuộc vào". Ba điều cốt lõi cần nhớ:

  • SemVer là hợp đồng. MAJOR = breaking (phải sửa), MINOR = thêm mới tương thích ngược (cứ nâng), PATCH = sửa lỗi (chỉ tốt hơn). Khi nghi ngờ, chọn mức nghiêm trọng hơn.
  • Đừng phá vỡ niềm tin. Deprecate trước, giữ song song, xóa sau ở MAJOR. Luôn cung cấp đường lui và migration guide. Changelog phải viết cho con người.
  • Một ngôn ngữ version chung cho Figma và code. Số version nội bộ tự động của Figma không thay thế SemVer; hãy đồng bộ một con số có ý nghĩa cho cả hai mặt thiết kế và code.
Nắm vững versioning, bạn không chỉ tránh được những buổi trưa "cháy nhà" như ở đầu bài — bạn còn xây dựng được uy tín của một người vận hành design system chuyên nghiệp mà mọi team đều muốn hợp tác.