Vượt qua những câu lệnh: Cách Git Hooks kiểm soát AI Coding Agents trong môi trường sản xuất

05 tháng 4, 2026·9 phút đọc

Tại Fleek, cả kỹ sư viên và nhân viên phi kỹ thuật đều xây dựng công cụ nội bộ bằng AI coding agents. Chúng tôi nhận thấy chỉ dùng file hướng dẫn là chưa đủ; Git Hooks mới là lớp thực thi quyết định để đảm bảo tuân thủ quy chuẩn và thông báo lỗi đóng vai trò như prompt cho AI.

Vượt qua những câu lệnh: Cách Git Hooks kiểm soát AI Coding Agents trong môi trường sản xuất

Tại Fleek, cả kỹ sư viên và nhân viên phi kỹ thuật đều xây dựng các công cụ nội bộ sử dụng AI coding agents. Chúng tôi nhận ra rằng các tệp hướng dẫn đơn lẻ là chưa đủ — AI chủ yếu tuân theo chúng, nhưng không phải lúc nào cũng vậy. Git Hooks đã cung cấp cho chúng tôi một lớp thực thi mang tính quyết định (deterministic) ở trên cùng. Bài viết này sẽ đi sâu vào cách chúng tôi thiết kế chúng và lý do tại sao thông báo lỗi lại quan trọng hơn bạn nghĩ.

Heuristic vs DeterministicHeuristic vs Deterministic

Vấn đề cốt lõi

Chúng tôi vận hành một nền tảng công cụ nội bộ — một monorepo với hơn 10 ứng dụng con. Những người xây dựng trên nền tảng này dao động từ các kỹ sư cấp cao đến các thành viên trong đội ngũ kinh doanh chưa bao giờ viết mã và sử dụng Claude Code làm công cụ phát triển chính.

Trong một đội ngũ kỹ thuật truyền thống, bạn thực thi các tiêu chuẩn thông qua quá trình review mã (code review) và kiến thức ngầm của tập thể. Một kỹ sư nhìn thấy một hook bị lỗi và suy nghĩ: "À đúng rồi, mình cần sửa cái này." Họ có ngữ cảnh. Họ hiểu codebase.

Một tác nhân AI thì không có bất kỳ điều nào trong số đó. Nó không biết các quy ước của bạn. Và ngay cả khi bạn viết hết tất cả ra trong một tệp hướng dẫn — nó vẫn có thể mắc sai lầm.

Sự khác biệt giữa Heuristic và Deterministic

Có hai cách để thực thi các quy tắc trong một codebase được hỗ trợ bởi AI: bạn có thể nói với AI cần làm gì (heuristic - phỏng đoán), hoặc bạn có thể khiến việc làm sai trở nên bất khả thi (deterministic - xác định).

Khi bạn viết một quy tắc trong CLAUDE.md như "sử dụng @fleekit/logger thay vì console.log", bạn đang đưa cho AI một lời gợi ý mạnh mẽ. Nhưng nó mang tính phỏng đoán. AI sẽ tuân theo nó hầu hết thời gian — có thể là 90%, có thể là 95%. Nhưng trong một codebase đủ phức tạp, với đủ nhiều người đóng góp, cuối cùng nó sẽ bỏ qua quy tắc này.

Một pre-commit hook quét các tệp đã staged để tìm console.log và chặn commit lại mang tính xác định. Nó kích hoạt mỗi lần. Nó không bao giờ quên. Nó không đưa ra ngoại lệ. Mã của AI đơn giản là không thể đạt được kho lưu trữ nếu không vượt qua được kiểm tra này.

CLAUDE.md = "Đây là cách chúng tôi làm việc" (hướng dẫn) Git hooks = "Bạn hoàn toàn không thể làm theo cách khác" (thực thi)

Bạn cần cả hai. Tệp hướng dẫn dạy cho AI cách làm đúng. Hook sẽ bắt nó khi nó quên.

Thông báo lỗi của Hook chính là Prompt

Đây là điều ít người nói tới: thông báo lỗi CHÍNH LÀ kỹ thuật prompt engineering.

Khi một hook chặn một commit, AI sẽ đọc đầu ra lỗi và sử dụng nó để khắc phục sự cố. Điều đó có nghĩa là chất lượng thông báo lỗi của bạn quyết định trực tiếp việc AI tự sửa chữa hay đi vào vòng xoáy sửa sai mãi mãi.

Các hook truyền thống được thiết kế cho con người. Chúng xuất ra một cái gì đó giống như:

Error: console.log not allowed

Một con người sẽ suy nghĩ: "À đúng rồi, chúng ta dùng custom logger." Họ mang theo ngữ cảnh của riêng mình.

Một AI nhìn thấy thông báo đó và không biết phải làm gì. Nó có thể xóa log hoàn toàn. Nó có thể thay thế nó bằng process.stdout.write. Nó có thể nhờ người dùng giúp đỡ.

Bây giờ hãy xem cách chúng tôi thiết kế đầu ra hook của mình:

╔══════════════════════════════════════════════════════════════╗ ║ console.log DETECTED — commit blocked ║ ╚══════════════════════════════════════════════════════════════╝

Found console.* calls in app code:

apps/large-buyer/fe/pages/index.tsx:42: console.log(data)

RULE: Use @fleekit/logger instead of console.* in app code. import { createLogger } from "@fleekit/logger"; const log = createLogger("my-tool"); log.info({ key: "value" }, "Message here");

Thông báo lỗi thân thiện với AIThông báo lỗi thân thiện với AI

Ba điều xảy ra ở đây:

  1. Nêu rõ quy tắc — sử dụng @fleekit/logger
  2. Chính xác nơi vi phạm — tệp, số dòng, nội dung
  3. Đưa ra mã thay thế — import, khởi tạo, cách sử dụng

AI đọc thông báo lỗi này, hiểu được ràng buộc và sửa nó đúng ngay trong lần thử lại đầu tiên. Không cần con người can thiệp. Thông báo lỗi hook đã trở thành một prompt.

Điều này thay đổi cách bạn thiết kế các hook. Bạn không còn viết thông báo lỗi cho các kỹ sư nữa. Bạn viết chúng cho một AI sẽ phân tích cú pháp đầu ra, trích xuất quy tắc và áp dụng bản sửa lỗi. Hãy cụ thể. Hãy hoàn chỉnh. Hãy chỉ ra giải pháp, không chỉ nêu vấn đề.

Giữ cho tài liệu "sống"

Hook có thể thực thi nhiều thứ hơn là chất lượng mã — chúng có thể giữ cho ngữ cảnh của riêng AI chính xác.

Mọi ứng dụng trong monorepo của chúng tôi đều có một CLAUDE.md — một tệp mô tả mục đích của công cụ, mô hình dữ liệu, logic kinh doanh và các lưu ý quan trọng. Đây là thứ cung cấp ngữ cảnh cho AI khi nó làm việc trên một công cụ.

Vấn đề là: tài liệu dễ bị lỗi hóa (rot). Trong mọi codebase tôi từng làm việc, tài liệu bắt đầu mạnh mẽ nhưng sau đó bị lệch pha. Mã thay đổi, nhưng tài liệu thì không. Sáu tháng sau, tài liệu lại gây hiểu lầm.

Chúng tôi đã viết một pre-commit hook kiểm tra: nếu bạn thay đổi mã trong một ứng dụng, bạn có cũng cập nhật CLAUDE.md của ứng dụng đó không?

Nếu CLAUDE.md hoàn toàn không tồn tại, commit sẽ bị chặn:

╔══════════════════════════════════════════════════════════════╗ ║ MISSING CLAUDE.md — commit blocked ║ ╚══════════════════════════════════════════════════════════════╝

The following app(s) have code changes but no CLAUDE.md: • apps/large-buyer/

Every app must have a CLAUDE.md describing its purpose, data sources, business logic, and gotchas.

TO FIX: Create apps/large-buyer/CLAUDE.md before committing.

Nếu nó tồn tại nhưng không được cập nhật cùng với các thay đổi mã, bạn sẽ nhận được cảnh báo:

⚠ CLAUDE.md not updated for: • apps/large-buyer/CLAUDE.md

Consider updating it if your changes affect the tool's behavior.

Điều này tạo ra một vòng phản hồi (feedback loop):

  1. AI đọc CLAUDE.md để hiểu công cụ
  2. AI thực hiện thay đổi mã
  3. Hook hỏi: "Bạn đã cập nhật tài liệu chưa?"
  4. AI cập nhật CLAUDE.md
  5. Phiên làm việc AI tiếp theo đọc CLAUDE.md đã cập nhật

Vòng phản hồi tài liệuVòng phản hồi tài liệu

Tài liệu luôn được cập nhật vì hệ thống yêu cầu nó tại thời điểm commit. Không phải thông qua kỷ luật, không phải thông qua lời nhắc trong code review — mà thông qua việc thực thi mang tính xác định.

Đây là hook có tỷ suất hoàn vốn (ROI) cao nhất mà chúng tôi xây dựng. Chỉ với vài dòng bash, tệp ngữ cảnh của mọi công cụ đều giữ được độ chính xác. Nó hoạt động cho các phiên làm việc của AI. Nó hoạt động cho các kỹ sư là con người. Nó hoạt động cho thành viên đội ngũ kinh doanh sử dụng Claude để thêm một nút bấm và giờ đây phải ít nhất thừa nhận rằng tài liệu của công cụ có thể cần cập nhật.

Tại sao không sử dụng Built-in Hooks của Claude Code?

Claude Code có các hook riêng — PreToolUse, PostToolUse, v.v. — để chặn các hành động của AI theo thời gian thực. Vậy tại sao không dùng những cái đó?

Phạm vi (Scope). Các hook của Claude Code nhìn thấy các lệnh gọi công cụ riêng lẻ — tệp này đang được chỉnh sửa, lệnh này đang được chạy. Git hooks nhìn thấy changeset — tổng thể của mọi thứ đã diễn ra.

Một hook của Claude Code không thể trả lời câu hỏi: "Branch này có chạm đến nhiều ứng dụng không?" hoặc "Mã đã thay đổi nhưng tài liệu thì không?". Những câu hỏi đó đòi hỏi việc so sánh tất cả các tệp đã staged với lịch sử của branch. Đó là mối quan tâm ở mức độ git.

Quan trọng hơn, git hooks áp dụng cho tất cả mọi người — mọi kỹ sư, mọi công cụ AI, mọi hệ thống CI. Chúng là phổ quát.

Cách tiếp cận đúng đắn là sử dụng cả hai. Hooks của Claude Code để hướng dẫn theo thời gian thực. Git hooks để thực thi mang tính xác định trên đầu ra.

Bài học chúng tôi học được

  • Thông báo lỗi là prompt. Đầu ra lỗi của bạn càng tốt, AI càng cần ít lần thử lại. Nêu rõ quy tắc, chỉ ra vi phạm, cung cấp lệnh sửa lỗi.
  • Chặn, đừng chỉ cảnh báo. Ban đầu chúng tôi đưa ra một số hook mang tính tư vấn. AI đã phớt lờ mọi cảnh báo. Nếu nó đủ quan trọng để kiểm tra, hãy chặn commit lại.
  • Mọi hook đều cần cách vượt qua. Chúng tôi sử dụng các biến môi trường SKIP_{CHECK_NAME}=1. Đôi khi con người biết rõ hơn và một hook không nên trở thành vật cản cho người hiểu được sự đánh đổi.
  • Hook tài liệu có ROI cao nhất. Nó gần như không tốn chi phí và giữ cho các tệp ngữ cảnh chính xác cho mọi phiên trong tương lai — con người hay AI.

Chúng tôi hiện chạy tổng cộng 9 hook trên pre-commit, commit-msg và pre-push. Chúng thực thi quy tắc đặt tên branch, ngăn rò rỉ bí mật, đảm bảo một ứng dụng trên mỗi branch, xác thực định dạng commit message, kiểm tra cấu trúc repo và nhiều hơn nữa. Không phải tất cả chúng đều thú vị để viết về — nhưng cùng nhau, chúng tạo thành một lớp xác định khiến các đóng góp của AI trở nên đáng tin cậy ở quy mô lớn.

Nếu đội ngũ của bạn đang sử dụng AI coding agents, git hooks của bạn giờ đây là một phần của chiến lược AI của bạn. Tệp hướng dẫn dạy cho AI các quy ước của bạn. Các hooks thực thi chúng. Hãy đối xử với chúng tương ứng.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗