Rào chắn cho phiên code AI trên site này
Ba sự cố thật trên brandon-ta.com, từ bài blog bịa số liệu đến node_modules bị xóa, và những quy tắc viết ra để chặn từng sự cố.
Site này được xây dựng và bảo trì bằng các phiên code với AI. Cách làm đó chỉ ổn khi các phiên chạy theo quy tắc viết sẵn, và mỗi quy tắc dưới đây tồn tại vì có một sự cố xảy ra khi chưa có nó. Bài này lấy ba sự cố làm trục. Cả ba đều có trong lịch sử git hoặc trong ghi chú mà các phiên lưu lại.
Sự cố 1: bài viết mô tả một site không tồn tại
Ngày 24/4/2026, commit 8c5837f thêm một bài thử nghiệm và ba bản nháp theo cụm, do một phiên AI viết. Các bài ở ngôi thứ nhất, ghi ngày sang tháng 5, và kể những câu chuyện có chi tiết bịa: một hệ thống chạy “5 tháng”, thời gian trung vị 9 ngày từ nháp đến đăng. Ngày 11/9/2026, một phiên AI đã đăng hai bài trong số đó mà không đối chiếu với chính site mà chúng mô tả.
Ngày 16/9, commit a9b51b7 (gộp trong PR #21) đưa cả hai về bản nháp. Thông điệp commit liệt kê điều sai. Một bài khẳng định trang blog của site “không có JavaScript phía client. Không hề.” Thực tế một trang bài viết được sinh ra tải 65,8 KB JavaScript đã nén gzip và hydrate hai island. Bài đó cũng kể “nút đổi giao diện” trong số các island, trong khi tài liệu thiết kế cấm nút này và nó không tồn tại.
Bài về lựa chọn Astro sau đó được viết lại bằng số đo trên bản build production trong ngày và đăng lại. Bài về việc đăng 32 bài mỗi năm vẫn ở dạng nháp, vì không còn gì đúng để dựng lại.
Quy tắc rút ra. Ghi chú dự án giờ yêu cầu kiểm tra nguồn gốc của bài trong lịch sử git trước khi đăng hoặc dịch, và đo mọi khẳng định về site bằng kết quả thật. Với dung lượng trang, đo trong trình duyệt thay vì tìm trong HTML, vì các island đánh dấu client:visible chỉ tải khi người đọc cuộn tới. Một ghi chú liên quan yêu cầu xác nhận khẳng định về sản phẩm trong repo của chính sản phẩm đó, không dựa vào tài liệu kế hoạch vốn viết quy tắc và dự định cùng giọng với sự thật.
Sự cố 2: một dấu “xong” chưa ai xem nó chạy
Sự cố đầu tiên thực chất là thiếu bằng chứng. Các bài đọc rất trơn, nhưng không ai chạy thứ mà chúng mô tả. Chỉ dẫn toàn cục cho các phiên của tôi trả lời thẳng điều này:
No fix, and no “it works”, without having watched the real thing run.
(Tạm dịch: không sửa lỗi và không nói “chạy được” nếu chưa xem chính thứ đó chạy thật.)
Với lỗi, thứ tự cố định: tái hiện, tìm đúng dòng sai, sửa thay đổi nhỏ nhất, rồi chạy lại đúng bước tái hiện. Với tính năng, chạy luồng như một người dùng. Cách báo cáo cũng có khuôn: “fixed (could not reproduce, unverified)” là trung thực, còn một chữ “fixed” trơn thì không.
Một quy tắc nữa giúp kiểm tra được điều này. Mỗi mục nghiệm thu được đánh dấu cùng bằng chứng: một lệnh và kết quả quyết định, đường dẫn ảnh chụp màn hình hoặc một URL. Đánh dấu không có bằng chứng vẫn tính là chưa xong.
Đây không phải thói quen riêng. Tài liệu Claude Code của Anthropic viết: “Give Claude a check it can run: tests, a build, a screenshot to compare.” Nếu không có kiểm tra như vậy, cùng trang đó lưu ý, “looks done” là tín hiệu duy nhất. Site này không có script test, nên phần kiểm tra là npm run astro check, một lần build production và một lượt duyệt trình duyệt trên bản build.
Sự cố 3: một lệnh npm ci làm trống nhầm thư mục

Ngày 5/10/2026, một git worktree có node_modules được tạo symlink tới bản của checkout chính để đỡ tốn lần cài. Một subagent chạy npm ci trong worktree đó. npm ci xóa node_modules trước khi cài và đi theo symlink, nên nó làm trống dependency của checkout chính. Checkout chính sau đó báo astro: command not found. Bản cài dở của worktree cũng gây lỗi 500 về ảnh, chỉ xuất hiện ở môi trường dev.
Bản thân worktree không phải vấn đề. Quy tắc toàn cục yêu cầu mỗi tác vụ một worktree, tạo từ đúng một checkout hiện có, không clone thứ hai hay sao chép thư mục, và xóa khi tác vụ đã gộp hoặc bị bỏ. Cách này giữ các tác vụ song song không giẫm lên file của nhau. Quy tắc thêm sau sự cố lấp đúng khoảng hở: không bao giờ symlink node_modules vào worktree, và chạy npm ci thật trong mỗi worktree mới, mất khoảng một phút.
Các quy tắc nhỏ giữ cho diff dễ review
| Quy tắc | Nằm ở đâu | Ngăn điều gì |
|---|---|---|
| Chỉ chạm vào những gì buộc phải chạm; mọi dòng đổi phải truy được về yêu cầu | Chỉ dẫn toàn cục, “Surgical changes” | Sửa lan man làm chìm thay đổi thật trong review |
| Dùng lượng code tối thiểu để giải quyết vấn đề | Chỉ dẫn toàn cục, “Simplicity first” | Tính năng đầu cơ và lớp trừu tượng dùng một lần |
| Không thêm thư viện nếu không được yêu cầu | Chỉ dẫn dự án | Phình dependency trên một site ưu tiên tĩnh |
| Nền tối duy nhất, một màu nhấn vàng, không có nút đổi giao diện | DESIGN.md | Phiên đưa lại thứ mà hợp đồng thiết kế đã bỏ |
Deploy bằng pull request từ develop vào production | Chỉ dẫn dự án | Nghĩ rằng push lên develop là xong; file workflow cũ nhắm tới nhánh main không tồn tại |
Tài liệu thiết kế quan trọng hơn vẻ ngoài. Nút đổi giao diện bịa ở Sự cố 1 bị phát hiện vì có một bản hợp đồng viết sẵn nói rằng nút đó không được tồn tại. Quy tắc mà phiên đọc được là quy tắc có thể dùng để đối chiếu.
Điều tôi rút ra
Đây là kết luận của tôi, không phải quy luật chung.
- Viết quy tắc sau khi thất bại, không phải trước. Mọi quy tắc ở đây truy được về một sự cố có ngày, nên dễ đánh giá xem nó còn cần không.
- Coi văn bản do AI soạn là đầu vào chưa kiểm chứng. Lỗi không nằm ở việc mô hình viết bài, mà ở chỗ không có gì buộc đối chiếu bài với site đang chạy.
- Làm cho phần kiểm tra là thứ phiên tự chạy được. Build, kiểm tra kiểu và một lượt trình duyệt tốn vài phút. Một khẳng định sai nằm trên site năm ngày tốn một pull request và một lần rút bài.
- Trạng thái dùng chung cần quy tắc về việc một tác vụ được chạm vào đâu. Hai checkout dùng chung một thư mục dependency thực ra là một checkout, chỉ thêm thủ tục.
Với người làm một sản phẩm nhỏ có AI hỗ trợ, phiên bản nhỏ nhất mà vẫn hữu ích là: một file chỉ dẫn ngắn, một ghi chú cho mỗi sự cố, và một quy tắc: chưa có gì là “xong” cho đến khi bạn xem nó chạy.
Viết bởi Brandon Ta
Solo dev ở Việt Nam, xây mười sản phẩm nhỏ một cách công khai, phần lớn cho doanh nghiệp ở Mỹ và Anh.
Bình luận
Tham gia thảo luận: chia sẻ suy nghĩ, đặt câu hỏi, hoặc trò chuyện với những người đọc khác ở bên dưới.