Tài liệu kiến trúc là thứ ai cũng biết nên có, nhưng gần như đội nào cũng để nó mục nát — và AI chính là cách thực tế nhất để bạn đảo ngược xu hướng đó mà không cần thêm một dòng ngân sách nào.
Bài Toán Nợ Tài Liệu
Bạn còn nhớ lần cuối cùng bạn mở một file ARCHITECTURE.md trong repo và thấy nó nói về một service đã bị xóa từ hai năm trước không? Đó không phải là chuyện hiếm — đó gần như là mặc định. Documentation debt (nợ tài liệu) là khoảng cách giữa những gì tài liệu nói và những gì hệ thống thực sự làm, và khoảng cách đó luôn có xu hướng giãn ra theo thời gian, không bao giờ tự thu hẹp.
Hãy hình dung tình huống rất thật này: một kỹ sư senior nghỉ việc sau ba năm gắn bó với hệ thống thanh toán. Anh ta là người duy nhất hiểu tại sao service payment-reconciliation lại gọi ngược vào order-service thay vì dùng event queue như mọi service khác — lý do là một race condition phát hiện năm 2023 mà không ai ghi lại. Kiến thức đó nằm hoàn toàn trong đầu anh ta, thứ mà giới kỹ thuật gọi là tribal knowledge (kiến thức bộ lạc — kiến thức chỉ tồn tại trong đầu một nhóm nhỏ người, không được văn bản hóa). Khi anh ta đi, kiến thức đó biến mất. Sáu tháng sau, một kỹ sư mới "dọn dẹp" đoạn code đó vì thấy nó "không nhất quán với pattern chung", và bug cũ quay lại y hệt.
Chi phí của documentation debt không trừu tượng chút nào. Một kỹ sư mới join team thường mất từ hai đến sáu tuần để "vẽ lại" kiến trúc hệ thống trong đầu mình — đọc code, hỏi han đồng nghiệp, đoán mò qua log. Nhân chi phí đó với số lượng người join mỗi năm, bạn sẽ thấy con số hàng chục nghìn đô la âm thầm chảy ra khỏi ngân sách engineering mà không ai lập dòng chi phí riêng cho nó. Chưa kể tới rủi ro: quyết định kiến trúc quan trọng bị đảo ngược một cách vô tình vì không ai nhớ nó từng được cân nhắc và bác bỏ.
Vấn đề cốt lõi không phải là kỹ sư lười viết tài liệu — mà là viết tài liệu tốn effort không tương xứng với lợi ích tức thời mà nó mang lại cho chính người viết. Bạn viết ADR (Architecture Decision Record — bản ghi quyết định kiến trúc) hôm nay để cứu một người lạ mặt sáu tháng sau, và bộ não con người không tối ưu cho kiểu đầu tư đó. Đây chính xác là chỗ AI thay đổi phương trình: khi chi phí viết giảm xuống gần bằng không, tỷ lệ lợi ích/chi phí đảo chiều hoàn toàn, và "viết tài liệu" không còn là việc phải hy sinh mới làm được nữa.
Mẹo: Trước khi bắt tay dùng AI viết tài liệu, hãy audit nhanh một lần: liệt kê 5 quyết định kiến trúc quan trọng nhất trong hệ thống hiện tại mà không có ADR nào ghi lại. Đó chính là backlog nợ tài liệu ưu tiên cao nhất của bạn — xử lý trước khi viết thêm gì mới.
Architecture Decision Record (ADR)
ADR là một tài liệu ngắn ghi lại một quyết định kiến trúc cụ thể: bối cảnh dẫn đến quyết định, quyết định đó là gì, và hệ quả của nó. Ý tưởng do Michael Nygard đề xuất năm 2011, và nó đã trở thành chuẩn mực trong ngành vì một lý do đơn giản: nó buộc bạn trả lời câu hỏi "tại sao" chứ không chỉ "cái gì". Code trả lời "cái gì" rất tốt. Nhưng code không bao giờ trả lời được "tại sao chúng ta không chọn MongoDB", "tại sao chúng ta bỏ GraphQL giữa chừng", hay "tại sao service này không dùng message queue như các service khác".
Một ADR tốt thường có cấu trúc: Title (tiêu đề), Status (trạng thái — proposed/accepted/deprecated/superseded), Context (bối cảnh), Decision (quyết định), Consequences (hệ quả — cả tích cực lẫn tiêu cực), và Alternatives Considered (các lựa chọn đã cân nhắc). Phần "Alternatives Considered" thường bị bỏ qua nhưng lại là phần giá trị nhất — nó ngăn người sau lặp lại cuộc tranh luận bạn đã giải quyết xong.
Dưới đây là một ví dụ ADR đầy đủ, viết cho một quyết định thực tế: chọn PostgreSQL thay vì MongoDB cho một dịch vụ Order trong hệ thống thương mại điện tử.
## Status
Accepted — 2026-03-14
## Context
Order Service hiện đang được thiết kế lại như một phần của việc tách monolith
thành các service độc lập (xem ADR-003: Service Decomposition Strategy).
Service này chịu trách nhiệm lưu trữ đơn hàng, trạng thái đơn hàng, lịch sử
thay đổi trạng thái, và liên kết với payment, inventory, và shipping.
Nhóm đã cân nhắc hai lựa chọn chính cho datastore: MongoDB (đang được dùng
cho Product Catalog Service) và PostgreSQL. Yêu cầu nghiệp vụ quan trọng:
- Một đơn hàng phải được ghi nhận đồng thời với việc trừ inventory và tạo
payment intent trong cùng một transaction logic, nếu không sẽ dẫn tới
tình trạng bán vượt tồn kho (oversell) — vấn đề đã xảy ra 3 lần trong Q4/2025.
- Đội Data Analytics cần chạy các truy vấn báo cáo phức tạp (join giữa
orders, order_items, customers, promotions) hàng ngày.
- Đơn hàng có schema tương đối ổn định: các trường chính (customer_id,
status, total_amount, line_items, shipping_address) không thay đổi
thường xuyên qua các sprint.
- Team hiện có 4 kỹ sư backend, có kinh nghiệm SQL sâu hơn NoSQL.
## Decision
Order Service sẽ dùng PostgreSQL 16 làm datastore chính, deploy qua Amazon
RDS Multi-AZ. Line items của đơn hàng sẽ được lưu dưới dạng bảng con
(`order_items`) có foreign key tới `orders`, thay vì nhúng JSON trong một
document như cách Product Catalog Service đang làm với MongoDB.
Chúng ta sẽ dùng transaction ACID của Postgres để đảm bảo tính nhất quán
giữa việc tạo order và giảm inventory reservation trong cùng một service
boundary (inventory reservation sẽ có bảng riêng trong cùng database ở giai
đoạn đầu, tách ra sau khi có nhu cầu scale riêng).
## Consequences
### Tích cực
- Transaction ACID giải quyết trực tiếp vấn đề oversell đã xảy ra 3 lần.
- Schema quan hệ giúp team Data Analytics viết SQL join trực tiếp, không
cần ETL phức tạp để "làm phẳng" document JSON.
- Kinh nghiệm SQL sẵn có của team giảm thời gian ramp-up và giảm rủi ro
thao tác sai (so với việc học MongoDB aggregation pipeline từ đầu).
- RDS Multi-AZ cho failover tự động, đơn giản hoá vận hành so với việc tự
quản lý MongoDB replica set.
### Tiêu cực
- Order Service sẽ cần schema migration (qua Flyway) mỗi khi thêm trường
mới — chậm hơn so với thêm field tự do vào MongoDB document.
- Không đồng nhất về công nghệ datastore trong hệ thống (Postgres cho
Order, MongoDB cho Catalog) — tăng chi phí vận hành vì team phải thành
thạo cả hai.
- Việc scale ghi (write) theo chiều ngang khó hơn MongoDB nếu volume đơn
hàng vượt quá khả năng của một primary instance — cần đánh giá lại sau
18 tháng nếu traffic tăng gấp 5 lần so với hiện tại.
## Alternatives Considered
### MongoDB (giữ nguyên như Product Catalog Service)
Ưu điểm: đồng nhất công nghệ, schema linh hoạt, dễ mở rộng ghi theo chiều
ngang qua sharding.
Lý do từ chối: MongoDB transaction đa document (multi-document ACID
transaction) có từ v4.0 nhưng hiệu năng kém hơn đáng kể so với transaction
trong một single-node relational database khi tần suất transaction cao
(ước tính > 50 transaction/giây ở giờ cao điểm). Rủi ro oversell không thể
chấp nhận được đối với nghiệp vụ core này.
### Event Sourcing với Kafka + PostgreSQL projection
Ưu điểm: audit trail tự nhiên, có thể replay toàn bộ lịch sử đơn hàng.
Lý do từ chối: độ phức tạp vận hành vượt quá nhu cầu hiện tại của team 4
người. Cân nhắc lại nếu yêu cầu audit trở nên khắt khe hơn (ví dụ do quy
định tài chính) hoặc team lớn hơn 8 người.
### DynamoDB
Ưu điểm: serverless, scale tự động, chi phí vận hành thấp.
Lý do từ chối: thiếu hỗ trợ join tự nhiên, gây khó khăn cho báo cáo phân
tích phức tạp mà đội Data Analytics yêu cầu; single-table design đòi hỏi
kinh nghiệm chuyên sâu mà team chưa có.
## Related
- ADR-003: Service Decomposition Strategy
- ADR-011: Inventory Reservation Consistency Model (superseded phần liên
quan đến oversell trong ADR này)
Lưu ý cách ADR này không né tránh phần tiêu cực. Một ADR chỉ toàn ưu điểm là ADR không đáng tin — nó thường là dấu hiệu người viết đang biện minh cho quyết định đã lỡ đưa ra, chứ không thực sự cân nhắc khách quan.
Mẹo: Đặt số ADR tăng dần và không bao giờ xóa ADR cũ — kể cả khi quyết định đã bị đảo ngược. Thêm ADR mới với status "Supersedes ADR-XXX" và trỏ ngược lại. Lịch sử các quyết định bị đảo ngược quý giá hơn bạn nghĩ, vì nó ngăn nhóm sau lặp lại chính sai lầm bạn đã sửa.
Sinh ADR Bằng AI
Đây là chỗ AI (agentic coding assistant như Claude Code hay Cursor) thực sự tỏa sáng, vì nó có khả năng đọc code, đọc lịch sử commit, đọc pull request discussion — những nguồn ngữ cảnh (context) mà con người thường phải tự nhớ lại một cách rời rạc. Nhưng có một cái bẫy: AI không có mặt trong cuộc họp nơi quyết định thực sự được đưa ra. Nó không biết bạn đã cân nhắc MongoDB rồi từ chối vì lý do chính trị nội bộ, hay vì một buổi demo thất bại. Vai trò của bạn không phải là để AI "bịa" ra bối cảnh, mà là cung cấp bối cảnh thật và để AI cấu trúc nó thành một ADR chuẩn mực.
Cách hiệu quả nhất: sau khi một quyết định kiến trúc được chốt (trong họp, trong Slack thread, trong PR review), bạn tổng hợp các điểm chính rồi nhờ AI viết thành ADR hoàn chỉnh.
Tôi vừa quyết định chọn PostgreSQL thay vì MongoDB cho Order Service mới.
Đây là các điểm chính từ buổi thảo luận:
- Lý do chính: cần ACID transaction để tránh oversell (đã xảy ra 3 lần
trong Q4/2025 khi dùng MongoDB cho Catalog)
- Data Analytics team cần SQL join phức tạp cho báo cáo
- Team có 4 kỹ sư, mạnh về SQL hơn NoSQL
- Đã cân nhắc và từ chối: MongoDB (rủi ro transaction), Event Sourcing
với Kafka (quá phức tạp cho team size hiện tại), DynamoDB (thiếu join)
- Sẽ dùng RDS Multi-AZ, line items là bảng con order_items
Hãy viết một ADR đầy đủ theo format Nygard (Title/Status/Context/Decision/
Consequences/Alternatives Considered/Related). Đánh số ADR-007. Ghi rõ cả
hệ quả tiêu cực, không chỉ tích cực. Trong Alternatives Considered, với
mỗi lựa chọn bị từ chối, nêu rõ ưu điểm của nó trước khi giải thích lý do
từ chối — để người đọc sau này thấy chúng ta đã cân nhắc công bằng.
Tham chiếu tới ADR-003 (Service Decomposition Strategy) trong phần Context.
Một prompt tốt luôn ép AI ghi cả nhược điểm — nếu không, bạn sẽ nhận về một tài liệu PR-marketing hơn là một ADR trung thực. Bạn cũng nên yêu cầu AI đọc trực tiếp code hiện tại khi có thể, thay vì chỉ dựa vào mô tả bằng lời của bạn:
Đọc schema hiện tại trong migrations/ folder và code trong
src/services/order/, so sánh với mô tả tôi vừa đưa. Nếu có điểm nào trong
code không khớp với những gì tôi mô tả (ví dụ: bảng inventory_reservation
đã tồn tại từ trước chứ không phải mới), hãy chỉ ra trước khi viết ADR.
Việc này biến AI thành một fact-checker (kiểm chứng thực tế) chứ không chỉ là công cụ viết văn — và đó là khác biệt giữa ADR hữu ích với ADR chỉ đẹp về hình thức.
Mẹo: Đừng bao giờ để AI tự "đoán" phần Context nếu bạn không cung cấp. Nếu bạn thấy AI viết những câu chung chung kiểu "quyết định này giúp cải thiện khả năng mở rộng", đó là dấu hiệu bạn chưa cho đủ ngữ cảnh cụ thể — hãy dừng và bổ sung số liệu, sự kiện thật.
Vẽ C4 Diagram Bằng Mermaid Và PlantUML
C4 model (context, container, component, code — bốn tầng trừu tượng của kiến trúc, do Simon Brown đề xuất) đã trở thành ngôn ngữ chung phổ biến nhất để mô tả kiến trúc hệ thống theo nhiều mức độ chi tiết khác nhau. Context diagram (sơ đồ ngữ cảnh) cho người ngoài thấy hệ thống của bạn tương tác với ai; container diagram (sơ đồ container) bóc tách thành các thành phần triển khai độc lập như service, database, message queue.
Cái hay của C4 khi kết hợp với AI là: bạn không cần vẽ tay trên Figma hay Lucidchart nữa. Bạn mô tả kiến trúc bằng lời, AI sinh ra code Mermaid hoặc PlantUML, và diagram đó sống ngay trong repo dưới dạng text — nghĩa là nó review được qua pull request, diff được qua git, và không bao giờ lệch pha với một file ảnh PNG bị quên cập nhật.
Lấy ví dụ một hệ thống học trực tuyến (online learning platform) — gần giống với chính nền tảng bạn đang học khóa này. Đây là context diagram bằng Mermaid, dùng cú pháp C4Context:
C4Context
title System Context diagram for Online Learning Platform
Person(student, "Student", "Người học đăng ký khóa học, xem bài giảng, làm bài tập")
Person(instructor, "Instructor", "Người tạo và quản lý nội dung khóa học")
System(learningPlatform, "Online Learning Platform", "Cho phép học viên đăng ký, học và theo dõi tiến độ khóa học")
System_Ext(paymentGateway, "Payment Gateway", "Xử lý thanh toán (Stripe)")
System_Ext(videoCdn, "Video CDN", "Phân phối video bài giảng (Cloudflare Stream)")
System_Ext(emailService, "Email Service", "Gửi email xác nhận, nhắc nhở (SendGrid)")
Rel(student, learningPlatform, "Đăng ký khóa học, xem bài giảng, nộp bài")
Rel(instructor, learningPlatform, "Tạo/chỉnh sửa nội dung khóa học")
Rel(learningPlatform, paymentGateway, "Xử lý thanh toán qua", "HTTPS/REST")
Rel(learningPlatform, videoCdn, "Upload và stream video qua", "HTTPS")
Rel(learningPlatform, emailService, "Gửi email qua", "SMTP API")
Và đây là chính hệ thống đó, cùng mức độ trừu tượng context, nhưng viết bằng PlantUML với thư viện C4-PlantUML:
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
LAYOUT_WITH_LEGEND()
title System Context diagram for Online Learning Platform
Person(student, "Student", "Người học đăng ký khóa học, xem bài giảng, làm bài tập")
Person(instructor, "Instructor", "Người tạo và quản lý nội dung khóa học")
System_Boundary(platformBoundary, "Online Learning Platform") {
System(learningPlatform, "Online Learning Platform", "Cho phép học viên đăng ký, học và theo dõi tiến độ khóa học")
}
System_Ext(paymentGateway, "Payment Gateway", "Xử lý thanh toán (Stripe)")
System_Ext(videoCdn, "Video CDN", "Phân phối video bài giảng (Cloudflare Stream)")
System_Ext(emailService, "Email Service", "Gửi email xác nhận, nhắc nhở (SendGrid)")
Rel(student, learningPlatform, "Đăng ký khóa học, xem bài giảng, nộp bài")
Rel(instructor, learningPlatform, "Tạo/chỉnh sửa nội dung khóa học")
Rel(learningPlatform, paymentGateway, "Xử lý thanh toán qua", "HTTPS/REST")
Rel(learningPlatform, videoCdn, "Upload và stream video qua", "HTTPS")
Rel(learningPlatform, emailService, "Gửi email qua", "SMTP API")
SHOW_LEGEND()
@enduml
Bạn để ý cả hai diagram mô tả cùng một hệ thống, cùng các actor, cùng các external system — chỉ khác cú pháp. Mermaid có lợi thế là render trực tiếp trên GitHub/GitLab mà không cần plugin, còn PlantUML mạnh hơn khi bạn cần diagram phức tạp nhiều tầng (container, component) với khả năng tùy biến style sâu hơn qua thư viện C4-PlantUML chính thức.
Prompt thực tế để sinh cả hai từ cùng một mô tả kiến trúc:
Dựa trên codebase hiện tại (đọc src/services/, docker-compose.yml, và
package.json của từng service), hãy sinh cho tôi:
1. Một C4 context diagram bằng Mermaid (cú pháp C4Context), thể hiện
platform chính, các loại người dùng (student, instructor), và các
external system mà platform gọi ra ngoài (payment, video CDN, email).
2. Cùng nội dung đó nhưng bằng PlantUML dùng thư viện C4-PlantUML
(!include C4_Context.puml), có dùng System_Boundary() để nhóm các
internal system nếu có nhiều hơn một.
Đảm bảo cả hai diagram nhất quán về tên actor, tên system, và các mối
quan hệ (Rel). Ghi chú giao thức (HTTPS, gRPC, SMTP...) trên mỗi Rel nếu
xác định được từ code.
Mẹo: Khi nhờ AI sinh C4 diagram, luôn yêu cầu nó trích dẫn file/dòng code cụ thể làm căn cứ cho mỗi mối quan hệ (Rel) trong diagram — ví dụ "Rel này dựa trên
axios.posttạipayment-client.ts:42". Việc này biến diagram từ "trông có vẻ đúng" thành "chứng minh được là đúng", và giúp bạn review nhanh hơn nhiều.
Viết System Overview Bằng AI
Nếu ADR trả lời "tại sao" cho một quyết định cụ thể, thì system overview (tài liệu tổng quan hệ thống) trả lời "cái gì" và "như thế nào" ở tầm nhìn toàn cục. Đây là tài liệu đầu tiên một kỹ sư mới nên đọc — nó cần trả lời được: hệ thống có bao nhiêu service, mỗi service làm gì, chúng giao tiếp với nhau ra sao, dữ liệu chảy từ đâu đến đâu, và những điểm "nóng" (hot path) nào cần cẩn trọng khi động vào.
Cái khó của system overview truyền thống là nó nhanh chóng lỗi thời vì viết một lần rồi không ai cập nhật. AI giải quyết vấn đề này theo hướng khác: thay vì viết một lần, bạn generate lại định kỳ từ chính trạng thái hiện tại của code, biến system overview từ tài liệu tĩnh thành sản phẩm phái sinh (derived artifact) của codebase.
Ví dụ prompt để sinh system overview từ đầu cho một hệ thống microservices:
Đọc toàn bộ cấu trúc repo này: docker-compose.yml, thư mục services/*,
các package.json/go.mod của từng service, và README hiện có (nếu có).
Viết một System Overview document gồm các phần:
1. Tổng quan một đoạn: hệ thống làm gì, phục vụ ai
2. Danh sách service, mỗi service: trách nhiệm chính, ngôn ngữ/framework,
datastore sử dụng
3. Sơ đồ luồng dữ liệu chính (data flow) cho use case quan trọng nhất
(ví dụ: học viên đăng ký khóa học) — dạng Mermaid sequence diagram
4. Danh sách các điểm tích hợp bên ngoài (external integration)
5. Các "điểm nóng" vận hành: service nào có SLA nghiêm ngặt nhất, service
nào là single point of failure nếu có
Với mỗi khẳng định, trích dẫn file/dòng code làm bằng chứng. Nếu không
tìm được bằng chứng cho một phần nào, ghi rõ "cần xác nhận với team" thay
vì đoán.
Chi tiết "ghi rõ cần xác nhận với team" cực kỳ quan trọng — nó ngăn AI tự tin sai (hallucination) và biến tài liệu thành nguồn thông tin sai lệch nguy hiểm hơn cả việc không có tài liệu. Một system overview sai còn tệ hơn không có system overview, vì nó tạo cảm giác an toàn giả.
Mẹo: Yêu cầu AI viết system overview theo văn phong "đây là những gì code nói", không phải "đây là những gì nên có". Hai văn phong này rất khác nhau — cái đầu mô tả thực trạng (kể cả khi thực trạng lộn xộn), cái sau dễ trở thành tài liệu mơ tưởng không khớp thực tế.
Giữ Tài Liệu Đồng Bộ Với Thay Đổi Code
Đây là phần khó nhất trong toàn bộ câu chuyện documentation, và cũng là phần mà công cụ AI thay đổi cuộc chơi nhiều nhất. Vấn đề không phải là viết tài liệu lần đầu — mà là giữ nó đúng qua hàng trăm pull request tiếp theo.
Cách tiếp cận truyền thống là "nhắc nhở" — thêm checklist trong PR template yêu cầu "cập nhật tài liệu nếu cần". Cách này thất bại gần như luôn luôn, vì "nếu cần" là một phán đoán chủ quan mà người review PR về feature thường bỏ qua khi đang bận tập trung vào logic nghiệp vụ.
Cách hiệu quả hơn: biến việc phát hiện doc drift (tài liệu lệch khỏi thực tế) thành một bước tự động, không phụ thuộc vào ký ức con người. Bạn có thể thêm một bước trong CI pipeline (hoặc chạy thủ công định kỳ) nơi AI so sánh diff của PR với tài liệu hiện có và gắn cờ khi phát hiện mâu thuẫn.
Đây là diff của PR #482 (dán diff vào, hoặc trỏ tới file diff).
Đây là nội dung hiện tại của docs/architecture/order-service-overview.md
và ADR-007.
So sánh: PR này có làm cho bất kỳ khẳng định nào trong hai tài liệu trên
trở nên sai lệch không? Ví dụ: đổi datastore, đổi giao thức giao tiếp giữa
service, thêm/xóa external dependency, đổi luồng dữ liệu chính.
Nếu có, liệt kê chính xác đoạn văn bản nào trong tài liệu cần sửa, và đề
xuất nội dung sửa. Nếu không có mâu thuẫn, trả lời ngắn gọn "Không phát
hiện doc drift" — đừng đề xuất sửa nếu không có mâu thuẫn thực sự.
Bạn có thể chạy prompt này như một GitHub Action step, gọi Claude qua API mỗi khi có PR động chạm tới thư mục services/ hoặc infra/, và post kết quả như một comment trên PR. Chi phí một lần gọi API rẻ hơn rất nhiều so với chi phí một kỹ sư mới đọc nhầm tài liệu sáu tháng sau.
Một cách khác, nhẹ hơn về hạ tầng: chạy một "audit" định kỳ (ví dụ hàng tuần qua cron job) quét toàn bộ tài liệu kiến trúc so với trạng thái code hiện tại, thay vì gắn vào từng PR riêng lẻ. Cách này phù hợp với team chưa sẵn sàng đầu tư CI phức tạp nhưng vẫn muốn có một lưới an toàn định kỳ.
Mẹo: Đừng cố gắng bắt AI phát hiện 100% doc drift ngay từ đầu. Bắt đầu với phạm vi hẹp — chỉ theo dõi các file trong
docs/architecture/khớp với các service có ADR liên quan — rồi mở rộng dần. Phạm vi quá rộng ngay từ đầu tạo ra quá nhiều false positive, khiến team nhanh chóng phớt lờ cảnh báo.
Dùng AI Giải Thích Kiến Trúc Hiện Có Cho Thành Viên Mới
Onboarding kỹ sư mới luôn là bài kiểm tra thực tế nhất cho chất lượng tài liệu kiến trúc của bạn. Và đây chính là use case mà AI tỏa sáng theo cách khác hẳn với việc viết tài liệu — thay vì tạo ra một tài liệu tĩnh, AI trở thành một "kỹ sư senior ảo" luôn sẵn sàng trả lời câu hỏi 1-1, không bao giờ mất kiên nhẫn dù bị hỏi lại câu tương tự lần thứ năm.
Điểm mạnh của cách tiếp cận này: một kỹ sư mới thường ngại hỏi đồng nghiệp những câu "có vẻ ngu ngơ" nhiều lần trong ngày đầu. Với AI, rào cản tâm lý đó biến mất. Nhưng bạn cần lưu ý: câu trả lời của AI chỉ tốt bằng nguồn nó được trỏ vào — nếu bạn để nó tự do đoán dựa trên kiến thức chung về "microservices thường như thế nào", nó sẽ đưa ra câu trả lời chung chung không khớp hệ thống thật của bạn.
Bạn là một kỹ sư senior đang onboard tôi vào hệ thống Online Learning
Platform. Tôi vừa join team hôm nay.
Hãy đọc: tất cả ADR trong docs/adr/, system overview trong
docs/architecture/, và cấu trúc thư mục services/*.
Giải thích cho tôi như thể tôi chưa biết gì về hệ thống này nhưng đã có
5 năm kinh nghiệm backend nói chung:
1. Bức tranh tổng thể: hệ thống này giải quyết bài toán gì, có bao nhiêu
service chính
2. Nếu tôi cần sửa luồng "học viên nộp bài tập", tôi cần động vào những
file/service nào, theo thứ tự nào
3. Có quyết định kiến trúc nào "trông kỳ lạ" nhưng thực ra có lý do lịch
sử cụ thể không? (trích ADR liên quan)
4. Tôi cần tránh làm gì trong tuần đầu để không phá vỡ invariant quan
trọng nào đó?
Nếu có thông tin bạn không tìm thấy trong tài liệu hoặc code, nói rõ là
"tài liệu chưa đề cập" thay vì suy đoán.
Câu hỏi số 3 và số 4 đặc biệt giá trị — chúng buộc AI đào sâu vào đúng loại tribal knowledge mà tài liệu truyền thống hay bỏ sót: những quyết định "kỳ lạ nhưng có lý do", và những cạm bẫy ngầm chưa ai viết thành cảnh báo rõ ràng.
Mẹo: Khuyến khích thành viên mới lưu lại các câu hỏi họ phải hỏi AI mà AI trả lời sai hoặc mơ hồ trong tuần đầu tiên. Danh sách đó chính xác là danh sách các khoảng trống tài liệu cần vá — hiệu quả hơn nhiều so với việc đoán xem tài liệu còn thiếu gì.
Quy Trình Living Documentation
Tất cả những kỹ thuật ở trên chỉ thực sự có giá trị khi chúng được ghép lại thành một quy trình lặp lại được, chứ không phải những lần dùng AI rời rạc, ngẫu hứng. Đây là mô hình living documentation (tài liệu sống) — tài liệu được coi như một sản phẩm phái sinh từ code, được tái tạo và kiểm tra liên tục thay vì viết một lần rồi bỏ quên.
Một quy trình end-to-end thực tế cho một team cỡ trung bình (10-30 kỹ sư) có thể trông như sau:
-
Tại thời điểm quyết định kiến trúc (trong họp, RFC, hoặc PR review lớn): kỹ sư tổng hợp ngắn gọn các điểm chính, dùng AI sinh ADR đầy đủ theo mẫu chuẩn, review và merge vào
docs/adr/. -
Trong CI, khi PR động chạm tới
services/hoặcinfra/: một GitHub Action step gọi AI so sánh diff với ADR và system overview liên quan, post comment cảnh báo nếu phát hiện doc drift. Không tự động block merge — chỉ cảnh báo, để tránh CI trở thành rào cản gây khó chịu. -
Hàng tuần, qua scheduled job: một audit rộng hơn quét toàn bộ
docs/architecture/so với trạng thái hiện tại của tất cả service, tạo issue tự động cho mỗi mục lệch pha được phát hiện, gán cho tech lead của service tương ứng. -
Hàng quý: dùng AI regenerate lại toàn bộ C4 diagram (context và container level) từ code hiện tại, diff với version cũ trong repo. Bất kỳ thay đổi lớn nào trong diagram (thêm/xóa external system, đổi container) là dấu hiệu kiến trúc đã trôi dạt so với thiết kế ban đầu — đáng để thảo luận trong buổi retro kiến trúc.
-
Khi có thành viên mới: dùng AI làm buổi "phỏng vấn ngược" — AI hỏi ngược lại tài liệu hiện có bằng các câu hỏi mà một newcomer thường hỏi, phát hiện khoảng trống trước khi người thật gặp phải chúng.
Điểm mấu chốt của quy trình này: không có bước nào đòi hỏi một kỹ sư ngồi xuống "viết tài liệu" như một task riêng biệt tốn nhiều giờ. Mỗi bước đều gắn liền với một sự kiện đã xảy ra tự nhiên trong vòng đời phát triển (quyết định, PR, lịch trình, onboarding) — documentation trở thành tác dụng phụ tự nhiên của công việc, không phải gánh nặng thêm vào.
Mẹo: Đừng để bước CI (mục 2) tự động block merge dựa trên cảnh báo doc drift — điều này gần như chắc chắn dẫn tới việc kỹ sư tìm cách "qua mặt" bước kiểm tra thay vì sửa tài liệu. Giữ nó ở dạng cảnh báo mềm, và chỉ escalate thành bắt buộc nếu có bằng chứng team đang phớt lờ cảnh báo liên tục.
Thực Hành: Sinh Một ADR Và Một C4 Diagram
Phần này hướng dẫn bạn từng bước tạo ra một ADR và một C4 context diagram hoàn chỉnh từ đầu, dùng một agentic coding assistant (Claude Code hoặc Cursor). Giả sử bạn vừa quyết định thêm một service mới: notification-service cho platform học trực tuyến, dùng để gửi thông báo khi có bài tập mới, deadline sắp tới, hoặc phản hồi từ instructor.
Bước 1 — Thu thập bối cảnh thô. Trước khi mở AI, viết ra (dù chỉ vài dòng gạch đầu ý) những gì bạn nhớ về cuộc thảo luận: lý do cần service riêng, các lựa chọn đã cân nhắc, ràng buộc kỹ thuật. Đừng bỏ qua bước này — đây là bước duy nhất AI không tự làm thay bạn được.
Ví dụ ghi chú thô của bạn:
- Cần notification-service riêng vì order-service đang gọi trực tiếp
SendGrid, làm chậm response time của API tạo đơn hàng
- Sẽ dùng message queue (đã có RabbitMQ sẵn trong hạ tầng) để decouple
- Cân nhắc: Firebase Cloud Messaging cho push notification, nhưng quyết
định hoãn lại, giai đoạn 1 chỉ làm email + in-app notification
- Team: 2 kỹ sư sẽ maintain service này
Bước 2 — Prompt AI sinh ADR.
Tôi vừa quyết định tách notification-service ra khỏi order-service.
Ghi chú thô:
[dán ghi chú ở Bước 1]
Viết ADR-012 theo format Nygard: Title/Status/Context/Decision/
Consequences/Alternatives Considered/Related. Trạng thái: Accepted,
ngày hôm nay. Trong Context, giải thích rõ vấn đề response time hiện tại
làm căn cứ. Trong Alternatives Considered, đề cập rõ Firebase Cloud
Messaging và lý do hoãn (không phải từ chối hẳn — ghi rõ "revisit sau
giai đoạn 1").
Kết quả rút gọn AI trả về (minh họa):
## Status
Accepted — 2026-07-09
## Context
Order Service hiện gọi trực tiếp SendGrid để gửi email xác nhận đơn hàng
ngay trong request tạo đơn hàng, khiến P95 latency của API tăng thêm
~800ms mỗi khi SendGrid có độ trễ cao...
## Decision
Tạo notification-service độc lập, nhận message qua RabbitMQ...
## Alternatives Considered
### Firebase Cloud Messaging cho push notification
Ưu điểm: hỗ trợ tốt push notification trên mobile...
Lý do hoãn: giai đoạn 1 tập trung email + in-app, revisit khi mobile app
ra mắt (dự kiến Q1/2027).
...
(Bạn review, sửa số liệu 800ms nếu chưa đo thực tế, rồi merge vào docs/adr/adr-012.md.)
Bước 3 — Prompt AI sinh C4 context diagram.
Dựa trên ADR-012 vừa viết và cấu trúc thư mục services/notification/
(nếu đã có code khung), sinh cho tôi một C4 context diagram bằng Mermaid
(C4Context) thể hiện: Order Service, Notification Service, RabbitMQ như
một message broker giữa chúng, và external system SendGrid. Thêm actor
Student nhận email.
Kết quả rút gọn:
C4Context
title Notification Flow Context
Person(student, "Student", "Nhận thông báo qua email/in-app")
System(orderService, "Order Service", "Tạo đơn hàng, publish event")
System(notificationService, "Notification Service", "Xử lý và gửi thông báo")
System_Ext(rabbitmq, "RabbitMQ", "Message broker")
System_Ext(sendgrid, "SendGrid", "Gửi email")
Rel(orderService, rabbitmq, "Publish OrderCreated event")
Rel(rabbitmq, notificationService, "Consume event")
Rel(notificationService, sendgrid, "Gửi email qua", "HTTPS/REST")
Rel(sendgrid, student, "Gửi email tới")
Bước 4 — Yêu cầu phiên bản PlantUML tương ứng để lưu song song trong wiki nội bộ nếu team bạn dùng Confluence (vốn hỗ trợ PlantUML tốt hơn Mermaid):
Chuyển diagram Mermaid ở trên sang PlantUML dùng thư viện C4-PlantUML,
giữ nguyên tên actor/system/relationship.
Bước 5 — Review và merge. Đọc kỹ cả ADR lẫn diagram, sửa số liệu chưa chính xác (AI có thể đoán con số 800ms — bạn cần thay bằng số đo thật nếu có), rồi merge vào docs/adr/ và docs/architecture/diagrams/. Đừng bỏ qua bước review — AI giúp bạn viết nhanh hơn 10 lần, nhưng trách nhiệm về tính chính xác vẫn thuộc về bạn.
Mẹo: Luôn thực hiện Bước 1 (ghi chú thô) trước khi mở AI, kể cả khi bạn thấy nó "mất thời gian thừa". Không có bước này, AI sẽ tự lấp đầy khoảng trống bằng suy đoán chung chung, và bạn sẽ mất nhiều thời gian sửa hơn là nếu bạn viết ghi chú thô từ đầu.
Những điểm chính
- Documentation debt không tự nhiên biến mất — nó là hệ quả của việc chi phí viết tài liệu luôn lớn hơn lợi ích tức thời cho người viết; AI đảo ngược phương trình này bằng cách giảm chi phí gần về không.
- ADR tốt luôn ghi cả hệ quả tiêu cực và các lựa chọn bị từ chối kèm lý do — một ADR chỉ toàn điểm tích cực là dấu hiệu đáng ngờ.
- AI chỉ nên cấu trúc hóa bối cảnh thật bạn cung cấp, không nên tự bịa ra lý do quyết định — luôn cho AI ghi chú thô cụ thể trước khi yêu cầu viết ADR.
- C4 diagram dạng text (Mermaid, PlantUML) sống trong repo, review được qua PR, và không bao giờ lệch pha như file ảnh tĩnh.
- Phát hiện doc drift nên là một bước tự động (CI hoặc audit định kỳ), không dựa vào checklist nhắc nhở con người vốn dễ bị bỏ qua.
- System overview nên mô tả "code nói gì" chứ không phải "nên có gì" — và luôn ghi rõ phần nào chưa xác nhận được thay vì để AI suy đoán.
- Dùng AI làm công cụ onboarding tương tác cho thành viên mới hiệu quả hơn tài liệu tĩnh, nhưng chỉ khi được trỏ đúng vào ADR và code thật của hệ thống.
- Living documentation là một quy trình gắn với các sự kiện tự nhiên trong vòng đời phát triển (quyết định, PR, lịch trình, onboarding), không phải một task viết tài liệu riêng biệt tốn nhiều giờ.