Chuỗi: docs-as-code
Năng suất & công cụ dev
Tự động hoá Docs-as-Code — để CI bắt lỗi tài liệu trước con người
Khi tài liệu đã nằm trong Git, bước tiếp theo là để máy canh giữ nó: validate link, schema, traceability, docs site, metrics và rollout theo giai đoạn cho đội lớn.
2026-06-2910 phút đọcVI
- 1.Git & Pull Request cho tài liệu — để docs đi cùng nhịp với code
- 2.Tự động hoá Docs-as-Code — để CI bắt lỗi tài liệu trước con người
- 3.Viết tài liệu bằng Markdown — từ file đẹp mắt đến tài liệu máy đọc được
- 4.Chiến lược Documentation-as-Code cho Super-App — capstone đầu-cuối
- 5.Docs-as-Code cheat sheet — một trang tóm tắt toàn bộ
- 6.Tại sao Documentation-as-Code — bài học từ một lỗi không ai thấy suốt cuối tuần
Bài này khép vòng vận hành: để CI bắt lỗi tài liệu, đo sức khỏe docs và mở rộng docs-as-code cho đội lớn.

Khi CI bắt được thứ mà mắt người bỏ sót
Ba tháng sau sự cố hoàn tiền của Minh (Lesson 1), đội Payments đã thêm CI vào quy trình tài liệu. Câu chuyện sau đó xảy ra đúng như họ kỳ vọng — chỉ theo hướng khác.
Một reviewer kỳ cựu của đội đang duyệt PR cập nhật hướng dẫn tích hợp hoàn tiền cho mini-app Booking. Anh đọc kỹ nội dung, kiểm tra tất cả các trường API, để lại vài nhận xét nhỏ, rồi nhấn Approve. Nhưng ngay lúc đó, CI bật đèn đỏ — validate-skills.sh báo lỗi: một link trong phần "Xem thêm" trỏ tới file refund-v1-legacy.md đã bị xoá từ sprint trước.
Reviewer ngạc nhiên. Anh đọc tài liệu hai lần mà không để ý một link ở cuối trang. Nếu không có CI, link gãy đó sẽ lọt vào nhánh chính — và nhà phát triển nào đó trong đội Booking sẽ bấm vào nó, nhận thông báo 404, rồi mất niềm tin vào toàn bộ hướng dẫn tích hợp.
Lần này không có sự cố. Chỉ có một dòng lỗi đỏ, một lần sửa nhỏ, và một PR được merge đúng giờ.
💡 Bài học: CI không thay thế reviewer — nó giải phóng reviewer. Máy lo mấy thứ máy móc, người lo thứ chỉ con người làm được: tài liệu này có đúng, có rõ, có đủ không?
Mục tiêu học tập
Sau bài này, bạn sẽ:
- Hiểu vì sao nên để CI (máy tự chạy kiểm tra mỗi khi có thay đổi) canh tài liệu, và khi nào CI tạo ra giá trị thật sự.
- Biết 3 loại kiểm tra tự động, và biết cách chạy từng loại ngay trên máy trước khi mở PR.
- Đọc và hiểu workflow GitHub Actions (dịch vụ chạy tự động việc của bạn mỗi khi có thay đổi trên GitHub) trong repo này từng dòng.
- Thiết lập được môi trường để bài tập CI chạy được — từ fork cho đến bật Actions.
- Nắm vài nguyên tắc giữ docs-as-code hoạt động tốt khi đội đông lên, kèm công cụ theo dõi sức khỏe tài liệu.
1. Vì sao để máy kiểm tra giúp
Ở Lesson 4, bạn đã biết tại sao tài liệu cần đi qua pull request (PR — yêu cầu hợp nhất để đồng đội xem trước khi gộp vào nhánh chính) và được reviewer kiểm. Nhưng người review dù kỹ đến đâu cũng hay bỏ sót một loại lỗi: những thứ lặp đi lặp lại, cơ học, không cần phán đoán.
Bạn không thể trông mong reviewer nhớ kiểm từng link trong một tài liệu dài 400 dòng. Bạn không nên để anh ta mất 5 phút đếm cột bảng Markdown để xem có lệch không. Và nếu quy ước frontmatter có 5 trường bắt buộc, đừng hy vọng ai đó ngồi đọc từng file để đảm bảo không trường nào bị thiếu.
Đây là việc của máy, không phải của người.
💡 Nguyên tắc cốt lõi: Máy bắt lỗi vặt, người lo phán đoán. CI là người gác cổng không bao giờ mệt, không bao giờ nhận email quan trọng đúng lúc đang review, và không bao giờ "để qua cho nhanh".
Khi CI lo phần cơ học, reviewer được rảnh tay tập trung vào câu hỏi thật: Tài liệu này có đúng với sản phẩm không? Có rõ với người đọc không? Có đủ thông tin để ai đó tự làm theo không?
2. Ba loại kiểm tra và cách chạy từng loại
Bộ kiểm tra tài liệu cơ bản chỉ gồm 3 loại. Bắt đầu với một loại là đã có giá trị — thêm dần sau.
| Kiểm tra | Bắt lỗi gì | Công cụ phổ biến |
|---|---|---|
| Lint | Định dạng Markdown sai (thiếu dòng trống, bảng lệch cột, tiêu đề nhảy cấp...) | markdownlint-cli2 |
| Link check | Link nội bộ hoặc ngoài bị gãy (như incident đầu bài) | lychee, markdown-link-check |
| Frontmatter | Thiếu trường bắt buộc (title, status, name, description...) | Script tự viết — repo này có sẵn |
2.1 Lint Markdown với markdownlint
Cài công cụ một lần, dùng mãi:
npm install -g markdownlint-cli2Tạo file cấu hình .markdownlint.json ở gốc repo để kiểm soát quy tắc nào bật, quy tắc nào tắt:
{
"default": true,
"MD013": false,
"MD033": false
}Giải thích hai dòng tắt: MD013 là giới hạn độ dài dòng (thường tắt vì tài liệu hay có bảng và link dài), MD033 là cấm HTML inline (tắt nếu bạn dùng callout HTML đôi khi). Các quy tắc còn lại (default: true) đều bật.
Chạy kiểm tra toàn bộ Markdown trong repo:
npx markdownlint-cli2 "**/*.md"Nếu có lỗi, công cụ in đường dẫn file, số dòng, và mã quy tắc — ví dụ docs/payments/refund-api.md:45 MD041/first-line-heading. Sửa rồi chạy lại cho tới khi không còn lỗi.
2.2 Kiểm link gãy với lychee
lychee là công cụ kiểm link viết bằng Rust — nhanh, chạy offline cho link nội bộ, và hiểu nhiều định dạng:
# Chạy qua npx (không cần cài toàn cục)
npx lychee --no-progress "**/*.md"Cờ --no-progress bỏ thanh tiến trình để output gọn hơn trong CI log. Nếu muốn bỏ qua kiểm link ngoài Internet (khi làm offline), thêm --offline.
Một thay thế nhẹ hơn nếu dự án chưa có Node: markdown-link-check:
npm install -g markdown-link-check
markdown-link-check docs/**/*.mdChọn cái nào cũng được — điều quan trọng là có kiểm link, không phải dùng công cụ nào.
2.3 Kiểm frontmatter với script của repo
Repo này có hai script kiểm frontmatter sẵn, chạy được ngay không cần cài thêm gì:
scripts/validate-skills.sh — kiểm chất lượng của mỗi skill (tài liệu năng lực đóng gói cho trợ lý AI). Nó làm ba việc theo thứ tự:
- Kiểm
.claude-plugin/plugin.jsoncó phải JSON hợp lệ không. - Kiểm
.claude-plugin/marketplace.jsoncó phải JSON hợp lệ không. - Duyệt qua mọi
skills/*/SKILL.mdvà xác nhận mỗi file có đủ hai trường frontmattername:vàdescription:.
Nếu bất kỳ kiểm tra nào thất bại, script in dòng ERR và thoát với mã lỗi khác 0 — tức là báo "có vấn đề, dừng lại".
bash scripts/validate-skills.shKết quả bình thường trông như thế này:
==> Validating plugin manifest
OK .claude-plugin/plugin.json is valid JSON
OK .claude-plugin/marketplace.json is valid JSON
==> Validating skills
OK docs-as-code-intro
==> All checks passedscripts/check-contracts.sh — kiểm frontmatter của mọi file *.contract.md (hợp đồng API giữa các đội). Mỗi contract phải khai báo đủ năm trường: id, version, status, provider, consumers. Thiếu một trường là contract không đáng tin cậy — script thoát lỗi và liệt kê trường thiếu.
bash scripts/check-contracts.shThử cố ý xoá trường version trong một contract rồi chạy lại — bạn sẽ thấy script báo đúng trường nào thiếu. Đây là cách nhanh nhất để hiểu một công cụ: phá nó, quan sát phản ứng, sửa lại.
📌 Thứ tự ưu tiên: Bắt đầu với
validate-skills.sh— nó chạy được ngay và bao gồm kiểm JSON + frontmatter skills. Thêmcheck-contracts.shkhi repo bắt đầu có nhiều contract. Thêm markdownlint và lychee khi đội quyết định chuẩn hóa định dạng.
3. Chạy tự động mỗi pull request với GitHub Actions
Chạy script ở máy thì tốt — nhưng người ta hay quên. Giải pháp: để máy chủ tự chạy mỗi khi có PR. Đó là việc của GitHub Actions.
GitHub Actions là gì? Đây là dịch vụ tích hợp sẵn trong GitHub, cho phép bạn định nghĩa "khi X xảy ra, hãy chạy Y". X có thể là mở PR, push code lên nhánh, hay đơn giản là theo lịch. Y là một chuỗi bước: lấy code về, cài công cụ, chạy script. Tất cả được viết trong một file YAML đặt trong thư mục .github/workflows/.
File workflow thật của repo này
Mở .github/workflows/validate.yml và bạn sẽ thấy:
name: validate
on:
pull_request:
push:
branches: [main]
jobs:
validate-skills:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Validate plugin manifest + skills
run: bash scripts/validate-skills.sh
- name: Verify contracts have required frontmatter
run: bash scripts/check-contracts.shĐọc file này như một câu văn: "Mỗi khi có pull request — hoặc có ai đó push vào nhánh main — hãy khởi một máy chủ Ubuntu, lấy toàn bộ code về, cài Python 3, rồi lần lượt chạy validate-skills.sh và check-contracts.sh."
Vài điểm đáng chú ý:
name: validate— tên của workflow, hiển thị trên giao diện GitHub khi bạn xem tab Actions.on: pull_request— trigger mỗi PR, dù PR đó nhắm vào nhánh nào. Không cần cấu hình thêm.jobs: validate-skills— tên job. GitHub hiển thị tên này trong danh sách "checks" ở PR.actions/checkout@v4— action chính thức của GitHub để lấy code về máy CI;@v4là phiên bản.- Bước cài Python là cần thiết vì
validate-skills.shgọipython3để kiểm tra JSON. Script tự phát hiện nếu Python không có (command -v python3) và cảnh báo thay vì lỗi — nhưng trên CI thì luôn muốn có Python để kiểm tra đầy đủ.
Kết quả hiển thị ngay trong PR
Sau khi bạn mở PR, GitHub tự chạy workflow. Kết quả hiện trong phần Checks ở cuối trang PR:
- ✅ Xanh — tất cả kiểm tra qua. Reviewer thấy CI xanh thì biết không cần lo phần cơ học, tập trung vào nội dung.
- ❌ Đỏ — có lỗi. GitHub chặn nút Merge và hiển thị chi tiết bước nào thất bại, output là gì. Tác giả sửa, push thêm commit, CI tự chạy lại.
⚠️ Quan trọng: Để CI chặn được merge, bạn cần bật branch protection cho nhánh
mainvà đánh dấu jobvalidate-skillslà required check. Lesson 4 đã giới thiệu branch protection — đây là nơi nó phát huy tác dụng thật sự. Không có branch protection, CI chỉ là cảnh báo tùy chọn, không phải rào cản.
Giờ tài liệu sai không thể lọt vào nhánh chính — dù người viết quên kiểm, dù reviewer đọc lướt. Đây chính là mảnh ghép số 4 ("CI tự kiểm tra") trong Lesson 1, giờ đã thành hình cụ thể và đang chạy trong repo này.
4. Thiết lập môi trường để bài tập chạy được
Trước khi vào bài tập, bạn cần 3 bước thiết lập. Nếu bạn đã từng làm việc với GitHub, hai bước đầu có thể đã xong.
Bước 4.1 — Tạo tài khoản GitHub
Truy cập github.com → Sign up. Chọn tên người dùng dễ nhớ (bạn sẽ dùng nó lâu dài). Xác nhận email. Xong.
Bước 4.2 — Fork repo về tài khoản của bạn
Fork (tạo bản sao repo vào tài khoản của mình) là cách đóng góp vào dự án bạn không có quyền ghi trực tiếp.
- Mở repo
docs-as-code-kittrên GitHub. - Nhấn nút Fork ở góc trên phải → chọn tài khoản của bạn làm đích.
- GitHub tạo một bản sao tại
github.com/<tên-của-bạn>/docs-as-code-kit. - Clone bản sao đó về máy:
git clone https://github.com/<tên-của-bạn>/docs-as-code-kit.git
cd docs-as-code-kitTừ đây, mọi thay đổi bạn push lên sẽ vào fork của bạn, không ảnh hưởng repo gốc.
Bước 4.3 — Bật GitHub Actions trong fork
GitHub mặc định tắt Actions trong fork để tránh chạy tốn tài nguyên khi không cần.
- Trong fork của bạn, vào tab Actions (thanh menu trên cùng của repo).
- GitHub hỏi xác nhận bạn muốn bật workflows — nhấn I understand my workflows, go ahead and enable them.
- Xong. Lần sau mở PR, Actions sẽ tự chạy.
📌 Nếu bạn không bật Actions, CI sẽ không chạy khi bạn mở PR — và bài tập phần CI sẽ không có kết quả để quan sát. Bước này nhỏ nhưng quan trọng.
5. Khi đội lớn dần
Docs-as-code chạy tốt với 3 người. Khi thành 30 người, vài nguyên tắc đơn giản giữ nó không loạn.
Tài liệu sống cạnh thứ nó mô tả. Tài liệu của mini-app Payments thì nằm trong mini-apps/payments/docs/ — không phải trên Confluence, không phải trong một thư mục docs/ chung mà mọi người đổ vào. Ai sửa API của Payments thì thấy ngay tài liệu Payments nằm cạnh đó, dễ cập nhật cùng lúc.
Mỗi mảng có người sở hữu. Dùng CODEOWNERS (đã học ở Lesson 4) để mỗi thư mục tài liệu có một đội chịu trách nhiệm. Không có "tài liệu vô chủ" — thứ không ai sở hữu là thứ không ai cập nhật.
Áp dụng từ từ, đừng ép một lần. Bắt đầu với một đội, một loại tài liệu. Khi họ thấy lợi — reviewer ít phải lo lỗi vặt, PR merge nhanh hơn — các đội khác sẽ muốn theo. Ép cả tổ chức đổi trong một tuần thường thất bại vì không ai thấy lý do.
Theo dõi vài con số đơn giản để biết sức khỏe tài liệu:
| Chỉ số | Câu hỏi nó trả lời | Cách đo đơn giản |
|---|---|---|
| Độ phủ (coverage) | Bao nhiêu phần đã có tài liệu? | Đếm thư mục có docs/ vs tổng thư mục |
| Độ tươi (freshness) | Tài liệu sửa lần cuối cách đây bao lâu? | git log --since="90 days ago" -- docs/ |
| Tỉ lệ đóng góp | Có bao nhiêu người thật sự sửa tài liệu? | git shortlog -sn -- docs/ |
Không cần công cụ phức tạp — ba lệnh trên chạy được ngay. Mục tiêu chỉ là phát hiện một mảng tài liệu đang bị bỏ quên trước khi nó trở thành documentation rot (tài liệu cũ dần và lệch khỏi sản phẩm đến mức gây hiểu sai).
Khi nào cần một trang web tài liệu (docs site)?
💡 Câu trả lời ngắn: chưa cần — cho đến khi thật sự cần.
Lúc đầu, đọc Markdown thẳng trên GitHub là đủ. Giao diện GitHub render Markdown đẹp, có thanh điều hướng file, tìm kiếm cơ bản, và hoạt động tốt cho đội kỹ sư nội bộ.
Bạn nên cân nhắc dựng docs site khi có ít nhất hai dấu hiệu này cùng lúc: tài liệu nhiều đến mức khó tra cứu; có người ngoài đội kỹ sư (đối tác, khách hàng, người dùng cuối) cần đọc thường xuyên. Lúc đó, một công cụ như VitePress hoặc Docusaurus sẽ giúp ích.
Với VitePress, ba lệnh để bắt đầu:
# Khởi tạo trong thư mục tài liệu
npx vitepress init
# Chạy local để xem kết quả
npx vitepress dev
# Build tĩnh để triển khai
npx vitepress build⚠️ Đừng để việc dựng docs site cản việc viết tài liệu. Một trang web đẹp nhưng trống rỗng không giúp được ai. Tài liệu tốt trên GitHub còn hơn tài liệu kém trên website đẹp.
🛠 Bài tập thực hành
Bài tập này chia làm hai phần. Phần A làm một mình được với repo và fork của bạn. Phần B là câu hỏi suy ngẫm — không cần code.
Phần A — Chạy kiểm tra local và thử CI
A1 — Clone và chạy script ngay trên máy.
Sau khi fork và clone repo về (xem §4), chạy:
bash scripts/validate-skills.sh
bash scripts/check-contracts.shXác nhận cả hai script exit 0 (tất cả đều pass). Bạn vừa làm đúng thứ CI sẽ làm — chỉ là trên máy chủ thay vì máy bạn.
A2 — Cố ý phá frontmatter để xem script phản ứng.
Mở file skills/docs-as-code-intro/SKILL.md. Xoá tạm thời dòng description: (chỉ xoá trong editor, chưa commit). Chạy lại:
bash scripts/validate-skills.shScript phải in một dòng ERR và thoát với mã lỗi khác 0. Phục hồi dòng description: và chạy lại để xác nhận về trạng thái xanh.
Bạn vừa tận mắt thấy cơ chế chặn lỗi hoạt động — không chỉ đọc về nó.
A3 — Mở PR với lỗi cố ý và quan sát CI
⚠️ Cần đã bật GitHub Actions trong fork theo §4.3 trước khi làm bước này.
Thực hiện sáu bước dưới đây theo thứ tự:
-
Bước 1 — Tạo branch mới:
git checkout -b test/broken-contract -
Bước 2 — Mở file
examples/nova-superapp/contracts/bff-refund.contract.mdvà xoá tạm dòngversion:trong frontmatter. -
Bước 3 — Commit và push:
git add examples/nova-superapp/contracts/bff-refund.contract.md git commit -m "test: cố ý xoá version để kiểm CI" git push -u origin test/broken-contract -
Bước 4 — Mở PR từ fork của bạn trên GitHub (GitHub thường gợi ý tự động khi bạn push nhánh mới).
-
Bước 5 — Chờ khoảng 30–60 giây. Vào tab Checks của PR — bạn sẽ thấy job
validate-skillsbáo đỏ, với output chỉ rõ file và trường thiếu. -
Bước 6 — Sửa lại file (thêm lại dòng
version:), commit thêm một lần nữa, push — CI tự chạy lại và chuyển sang xanh.
👉 Tham khảo đáp án:
examples/nova-superapp/.github/workflows/validate.ymllà workflow mẫu cho một super-app thật. Bạn có thể so sánh với workflow gốc của repo kit tại.github/workflows/validate.ymlđể thấy cách mở rộng thêm check theo quy mô dự án.
Phần B — Câu hỏi suy ngẫm
Viết 3–4 câu: nếu đội bạn tăng từ 3 lên 30 người, bạn sẽ áp dụng nguyên tắc nào ở §5 trước tiên, và vì sao? Hãy liên hệ với ít nhất một đặc điểm cụ thể của đội bạn (hoặc đội giả định Payments/Booking trong NovaApp).
Tóm tắt
- Nguyên tắc: Máy bắt lỗi vặt (định dạng, link gãy, thiếu frontmatter), người lo phán đoán (đúng, rõ, đủ). CI là tuyến phòng thủ không biết mệt.
- Ba loại check: lint (
npx markdownlint-cli2 "**/*.md"), link (npx lychee --no-progress "**/*.md"), frontmatter (bash scripts/validate-skills.sh+bash scripts/check-contracts.sh). Bắt đầu với một loại. - Workflow GitHub Actions trong
.github/workflows/validate.yml: triggerpull_request+push: branches: [main], jobvalidate-skills, chạy hai script theo thứ tự. - Thiết lập fork: tạo tài khoản GitHub → fork repo → clone → bật Actions trong fork. Ba bước, một lần.
- Khi đội lớn: tài liệu sống cạnh code, mỗi mảng có owner (
CODEOWNERS), áp dụng từ từ, theo dõi 3 chỉ số (coverage / freshness / contribution). Docs site chỉ dựng khi thật cần.
Đọc tiếp
- Lesson 6 — Capstone: ghép cả 5 bài thành một chiến lược hoàn chỉnh cho super-app NovaApp — từ kiến trúc thư mục, quản lý hợp đồng API, đến CI/CD tài liệu đầy đủ, kèm một case study đi từ đầu đến cuối.
- Thử ngay: chạy
bash scripts/validate-skills.shtrong repo này và đọc từng dòng output — giờ bạn hiểu từng dòng nó in ra có nghĩa gì. - Xem ví dụ thật: mở
examples/nova-superapp/để thấy một repo super-app được tổ chức theo mọi nguyên tắc bạn đã học từ Lesson 1 đến Lesson 5.
