Pillar cuối cùng là pillar mà các team trì hoãn lâu nhất. Lập luận của Godbolt rất đơn giản: frontend code đã lớn lên từ một stylesheet và vài lời gọi jQuery thành một hệ thống gồm abstraction, dependency và interface, vậy mà thói quen giữ kiến thức trong đầu từng người vẫn còn nguyên. Bỏ qua documentation là technical debt như mọi loại nợ khác, và một số tài liệu hữu ích nhất được sinh ra từ code chứ không phải viết tay.
Phần này cũng là nơi câu chuyện Red Hat được đền đáp. Case study trong chương 17 cho thấy một style guide lớn dần thành một rendering engine dùng chung rồi thành tự động hoá cho CMS, và đó là câu chuyện rõ ràng nhất mình biết về việc tooling cho tài liệu có thể quay lại định hình chính kiến trúc.
Ứng với chương 15 đến 17 trong sách, cùng phần kết ngắn ở chương 18.
Ý chính
Hai loại tài liệu
Phần mở đầu của phần này chia documentation thành hai nhóm. Static documentation (tài liệu tĩnh) là văn bản bạn viết, được một tool thu thập và xuất bản: Hologram lấy các khối Markdown từ comment trong file Sass hoặc JS và dựng một site có thể duyệt; SassDoc đọc các variable, function và mixin có chú thích và dựng tài liệu API cho một thư viện Sass. Code-driven documentation (tài liệu sinh từ code) do chính hệ thống tạo ra: một pattern library render template và style thật của bạn, và một JSON schema mô tả dữ liệu mà mỗi template cần đồng thời validate dữ liệu đó.
Sự phân biệt này quan trọng vì hai loại hỏng theo cách khác nhau. Tài liệu tĩnh bị cũ khi mọi người quên cập nhật, nên câu trả lời của sách là đặt chúng ngay cạnh code. Tài liệu sinh từ code không thể cũ theo cách đó, nhưng chúng chỉ mô tả code làm gì, không nói vì sao hay khi nào nên dùng. Một hệ thống khoẻ mạnh cần cả hai.
| Loại | Tool trong sách | Mạnh ở điểm nào | Hỏng theo cách nào |
|---|---|---|---|
| Tĩnh | Hologram, SassDoc | Ý định, guideline, onboarding, ghi chú API | Lệch khỏi code |
| Sinh từ code | Pattern Lab, JSON schema | Render thật, mọi variant, contract dữ liệu | Cho thấy cái gì, không cho thấy vì sao |
Tài liệu cạnh code, với một bề mặt public
Chương 15 đi qua cả hai tool tĩnh. Bài học lâu dài không nằm ở cấu hình của chúng. Thứ nhất, tài liệu nằm cùng file hoặc cùng thư mục với code nó mô tả sẽ được review trong cùng pull request. Thứ hai, việc SassDoc phân biệt thành phần public và private là một quyết định thiết kế, không phải chuyện định dạng: đánh dấu phần nội bộ là private cho bên dùng biết phần nào họ được phép phụ thuộc vào, nên bạn tự do refactor phần còn lại. Thứ ba, tự động vẽ ra dependency (mixin nào dùng function và variable nào) biến tài liệu thành công cụ điều hướng.
Trong một design system TypeScript, ba bài học đó vẫn áp dụng, với type system làm phần lớn công việc. File story nằm cạnh component, type của prop là bề mặt public, và trang tài liệu được sinh ra từ cả hai:
import type { Meta, StoryObj } from "@storybook/react-vite";
import { PriceTag } from "./PriceTag";
const meta = {
title: "Commerce/PriceTag",
component: PriceTag,
tags: ["autodocs"],
args: { label: "Monthly plan", amount: "$12" },
argTypes: {
tone: {
control: "inline-radio",
options: ["default", "sale"],
description: "Use sale only when a discount is active.",
},
},
} satisfies Meta<typeof PriceTag>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
export const Sale: Story = {
args: { tone: "sale", amount: "$9" },
};
export const OnDarkSurface: Story = {
args: { tone: "sale" },
globals: { theme: "dark" },
};
Mỗi story được export là một trạng thái được ghi tài liệu, đó là nửa sinh từ code. description giải thích khi nào dùng một variant, đó là nửa tĩnh. Global theme ở đây giả định setup Storybook của bạn có định nghĩa nó; ý chính là mọi context mà component hỗ trợ đều có story riêng.
Atomic Design và pattern library
Chương 16 giới thiệu Atomic Design của Brad Frost: atom (heading, input, image), molecule (một form tìm kiếm, một media block), organism (một header, một chuỗi comment), template (layout trang) và page (template được lấp nội dung thực tế). Pattern Lab biến các cấp đó thành một site có thể duyệt, trong đó mỗi pattern là một template đi kèm dữ liệu, và pattern lớn hơn include pattern nhỏ hơn đồng thời đổi data context mà nó truyền xuống.
Có hai điểm từ phần hướng dẫn đáng giữ lại. Pattern library nên dùng chung CSS và JS với site thật, để prototype dựng ở đó không cần style lại khi đưa vào CMS. Và trừu tượng hoá cả những đoạn markup nhỏ nhất, chẳng hạn mọi image đều render qua một template, sẽ có lợi khi về sau bạn cần đổi mọi instance cùng lúc. Luận điểm rộng hơn của Godbolt là xây từng trang khiến công việc khó dần theo thời gian, còn xây một hệ thống khiến nó dễ dần. Mình nghĩ cách phân loại năm cấp chặt chẽ ít quan trọng hơn thói quen ghép từ một kho dùng chung, và các nhóm phân loại lỏng hơn cũng ổn nếu team đã thống nhất.
Năm giai đoạn tài liệu của Red Hat
Chương 17 kể hệ thống đã lớn lên thế nào, và mình thấy trình tự này có giá trị hơn bất kỳ tool đơn lẻ nào:
Stage 1 Static style guide: docs blocks in each Sass partial, plus onboarding pages
Stage 2 Templates inside docs: one canonical template, included with different data
Stage 3 Library split from style guide: production and docs become equal consumers
Stage 4 Shared rendering engine: same templates in docs and CMS, validated by schemas
Stage 5 Generated editing UI: CMS fields created automatically from component schemas
Mỗi giai đoạn là phản ứng với nỗi đau của giai đoạn trước. Copy markup vào tài liệu thì bị cũ ngay lập tức, nên họ render tài liệu từ template. Phát hành library mỗi khi ai đó sắp xếp lại tài liệu thì quá ồn, nên họ tách repository, và một tác dụng phụ là style guide chỉ có thể đổi markup thông qua dữ liệu, y như production. Với một rendering engine duy nhất, developer backend không còn phải khớp markup bằng tay; họ tạo ra dữ liệu pass được schema. Cuối cùng team backend xây một importer biến schema thành entity của CMS, nên một pattern mới gần như không cần việc gì ở backend.
Bài học thầm lặng nằm ở sự ràng buộc. Godbolt ghi nhận rằng khi việc render đã bắt đầu, đòn bẩy duy nhất mọi người có là dữ liệu, không còn chỗ cho những chỉnh sửa lẻ tẻ vào markup hay thứ tự source, và chính hạn chế đó làm hệ thống vững chắc. Ông cũng ghi nhận rằng, khác với Pattern Lab, layout của họ không hardcode chứa component nào; dữ liệu quyết định. Theo cách nói của React, đó là một registry cộng với composition theo dữ liệu:
import type { ComponentProps, ComponentType } from "react";
import { PriceTag } from "./PriceTag";
import { Testimonial } from "./Testimonial";
const registry = {
priceTag: PriceTag,
testimonial: Testimonial,
} satisfies Record<string, ComponentType<any>>;
type Registry = typeof registry;
type Block = {
[Key in keyof Registry]: { type: Key; props: ComponentProps<Registry[Key]> };
}[keyof Registry];
export function Region({ blocks, columns }: { blocks: Block[]; columns: 2 | 3 | 4 }) {
return (
<div className="region" data-columns={columns}>
{blocks.map((block, index) => {
const Component = registry[block.type] as ComponentType<typeof block.props>;
return <Component key={index} {...block.props} />;
})}
</div>
);
}
Layout không biết gì về các phần tử con, số cột là một data attribute, và thêm một loại block chỉ là thêm một entry vào registry. Validate blocks đầu vào bằng schema ở ranh giới là bạn có phiên bản 2026 của design API của Red Hat.
Tiếp tục lặp
Phần kết ngắn: team cứ tự hỏi vì sao không xây hệ thống cuối cùng ngay từ ngày đầu, và câu trả lời là mỗi giai đoạn chỉ hiện ra sau khi giai đoạn trước đã xong. Hãy chờ đợi việc phải lặp lại, đừng đặt cược mọi thứ vào một tool cho tới khi nó đã chứng minh được mình nhiều lần, và chia sẻ những gì bạn học được.
Đọc lại ở năm 2026
- Hologram và SassDoc. Hologram là một Ruby gem từ một thời kỳ tooling khác, và mình sẽ không bắt đầu dự án mới với nó. SassDoc vẫn còn phù hợp nếu bạn phát hành một thư viện Sass, dù ngày càng ít team expose API Sass khi token và CSS custom property đã đảm nhận phần lớn giá trị dùng chung.
- Pattern library. Vai trò của Pattern Lab thường được Storybook đảm nhận trong các dự án React, Vue và Angular. Storybook 10, ra mắt cuối năm 2025, chuyển sang chỉ hỗ trợ ESM, và autodocs, controls cùng interaction test của nó bao quát phần lớn những gì sách phải ghép từ nhiều tool.
- Design tokens giờ là cách chính để ghi tài liệu cho bản thân visual language. Format 2025.10 của Design Tokens Community Group cho các tool như Style Dictionary, Tokens Studio và Terrazzo một format JSON chung, nên thang màu, khoảng cách và chữ có thể được ghi tài liệu và chuyển đổi từ một nguồn.
- Schema và CMS. JSON Schema (draft 2020-12) vẫn hoạt động tốt làm contract dữ liệu. Nhiều headless CMS và block editor mô hình hoá nội dung thành các block có type, nên giai đoạn 5 của Red Hat giờ gần với một việc cấu hình hơn là một importer tự viết, dù chi tiết phụ thuộc hoàn toàn vào CMS của bạn.
- Tài liệu tự test chính nó. Story giờ đồng thời là mục tiêu cho interaction test và visual test, khép kín vòng lặp giữa pillar này và pillar trước.
Áp dụng vào dự án
- Đặt tài liệu cạnh code: một file story và một ghi chú cách dùng ngắn trong mỗi thư mục component, được review trong cùng pull request.
- Quyết định cái gì là public. Chỉ export các component, prop và token bạn hỗ trợ; coi mọi thứ còn lại là nội bộ và nói rõ như vậy.
- Viết một story cho mỗi variant và mỗi context, để trang tài liệu đồng thời là danh sách mục tiêu cho visual test.
- Không bao giờ copy markup vào tài liệu. Render ví dụ từ component thật với dữ liệu.
- Sinh tài liệu token từ nguồn token thay vì duy trì một trang màu sắc riêng.
- Nếu nhiều app dùng hệ thống của bạn, hãy giữ library và site tài liệu là hai bên dùng tách biệt của cùng một package đã phát hành.
Tự kiểm tra
Điều gì phân biệt tài liệu tĩnh với tài liệu sinh từ code? Tài liệu tĩnh là văn bản do người viết và một tool thu thập; tài liệu sinh từ code do chính hệ thống đang chạy tạo ra, như pattern được render hay schema.
Vì sao việc đánh dấu thành phần là private lại quan trọng trong SassDoc hay một thư viện TypeScript? Nó xác định bề mặt public, để bên dùng biết họ được phụ thuộc vào cái gì và người bảo trì có thể refactor phần còn lại một cách an toàn.
Vấn đề gì đẩy Red Hat từ giai đoạn 1 sang giai đoạn 2? Markup copy vào tài liệu bị lỗi thời, nên họ render ví dụ từ một template chuẩn duy nhất.
Lợi ích phụ của việc tách library khỏi style guide là gì? Style guide và production trở thành hai bên dùng ngang hàng, chỉ có thể đổi output qua dữ liệu, điều này chứng minh mọi thứ dựng trong tài liệu sẽ chạy được trên production.
Một component registry tái hiện layout theo dữ liệu của Red Hat như thế nào? Layout render bất kỳ loại block nào mà dữ liệu liệt kê, nên cách ghép nội dung thay đổi mà không phải đụng vào code layout.
Đọc thêm
- Storybook 10 release notes: những gì thay đổi với bản release chỉ hỗ trợ ESM.
- Storybook docs: Autodocs: sinh trang tài liệu từ story và type.
- Atomic Design by Brad Frost: cuốn sách online của chính tác giả về phương pháp được dùng trong chương 16.
- Design Tokens Format Module 2025.10: format token ổn định để ghi tài liệu cho visual language.