Trừ bớt, Căn chỉnh, Chứng minh: Ba kỹ năng giữ cho AI agent không biến repo của bạn thành mớ bòng bong

Được xuất bản vào Jul 1, 2026 bởi Štefan Moravík.
AI Agents Developer Productivity Agentic Workflows Code Review
Trong trang này 15 phút đọc
  1. 1Phần 1 · CÁI GÌ — entropy là mặc định
  2. 2Phần 2 · VẬY THÌ SAO — năm PR, mỗi cái một bản năng
  3. 3Phần 3 · GIỜ THÌ SAO

Chúng tôi giao cho các agent lập trình những ticket thật mỗi ngày, và chúng gần như luôn ship ra thứ gì đó chạy được. Điều đó chưa bao giờ là nỗi lo. Nỗi lo là cách chúng làm việc: một helper mới nằm cạnh ba cái đã làm đúng việc đó, một abstraction mới toanh cho một caller duy nhất, một bản sửa vá đúng nhánh mà ticket nêu tên và để bốn nhánh anh em khác hỏng, một phát hiện kiểu “chắc là bug” mà không có bằng chứng theo hướng nào. Mọi thay đổi đều hợp lý ở cục bộ. Nhưng tổng hợp lại thì chúng là entropy.

TL;DR: Trên năm pull request Tier-3 trong lõi điều phối agent của chúng tôi, ba kỹ năng có thể ghép nối — ponytail (trừ bớt), grain (căn chỉnh), và review-proof (chứng minh) — đã xoá 821 dòng trong khi ship năm tính năng, bắt được một sự đứt gãy hợp đồng âm thầm mà chỉ dựa vào vẻ hợp lý thì đã bị merge, và giữ cho mọi thay đổi đi thuận theo thớ của kiến trúc hiện có. Dưới đây là việc của từng kỹ năng, kèm những diff thật để chứng minh.

Bỏ hết rào chắn đi và agent lập trình sẽ là một cỗ máy sinh ra những món nợ trông-có-vẻ-hợp-lý với tốc độ rất nhanh. Nên chúng tôi dựa vào ba kỹ năng nhỏ để đẩy theo hướng ngược lại — mỗi kỹ năng một bản năng: trừ bớt, căn chỉnh, chứng minh.

  • ponytail — viết thứ lười biếng nhất mà thực sự hoạt động. Xoá trước khi thêm. (Cái này không phải chúng tôi viết — đây là kỹ năng mã nguồn mở của Dietrich Gebert mà chúng tôi đã tuỳ biến cho stack của mình.)
  • grain / grain-review — đặt nó vào nơi mà codebase đã quyết định rồi. Đừng phát minh ra cách thứ hai.
  • review-proof — đừng tin một phát hiện cho đến khi nó tái hiện thành một test thất bại.

grain và review-proof chúng tôi xây dựng theo cùng tinh thần với ponytail, tinh chỉnh cho kiến trúc của mình. Mỗi PR trong năm PR dưới đây đều chạm vào lõi điều phối agent — nơi tốn kém nhất trong repo để mà làm ẩu — và mỗi cái đều được xếp hạng Tier 3: thêm độ phủ test và con người review chồng lên reviewer tự động, không có ngoại lệ.

Ba kỹ năng cho agent lập trình, mỗi cái một bản năng: ponytail trừ bớt, grain căn chỉnh, review-proof chứng minh — kèm thứ mỗi kỹ năng tiêu diệt và phần thưởng của nó

Ba kỹ năng trong một cái nhìn: ponytail trừ bớt, grain căn chỉnh, review-proof chứng minh.

Phần 1 · CÁI GÌ — entropy là mặc định

Đây là điều không ai cảnh báo bạn khi bạn giao một repo cho các AI agent: mỗi agent tối ưu cục bộ và không nhớ gì cả. Nó thấy ticket, chứ không thấy hàng tháng trời quy ước mà đội bạn đã hội tụ về. Nó không có lý do gì để thích dịch vụ email hiện có của bạn hơn một cái mới toanh, không có ký ức rằng tập trạng thái redirect đã được định nghĩa ở ba file khác, không có bản năng rằng “bản sửa nằm ở hàm dùng chung, không phải ở caller này.” Cứ để mặc, N agent sẽ tạo ra N cách hơi-khác-nhau để làm cùng một việc. Đó không phải là vấn đề chất lượng code mà bạn có thể lint cho sạch — đó là sự lệch hướng có tính cấu trúc và nó cộng dồn.

Ba kỹ năng, mỗi cái nhắm vào một trục của sự lệch hướng đó.

ponytail — thứ lười biếng nhất mà hoạt động

Ponytail là một lập trình viên kỳ cựu từng bị gọi lúc 3 giờ sáng vì abstraction thông minh của người khác, và từ chối viết cái tiếp theo. Nó chạy theo một cái thang và dừng ở nấc đầu tiên còn đứng vững: việc này có cần tồn tại không đã (YAGNI) → nó đã có trong codebase này chưa → thư viện chuẩn có làm được không → tính năng nền tảng có sẵn → dependency đã cài chưa → có thể gói trong một dòng không → chỉ khi đó mới đến, đoạn code tối thiểu mà hoạt động.

Nấc quan trọng nhất với agent là “nó đã có trong codebase này chưa?” Viết lại thứ đã nằm cách đó vài file là dạng rác agent phổ biến nhất. Và luật sửa bug của ponytail giữ cho diff trung thực: sửa nguyên nhân gốc, không sửa triệu chứng. Hãy grep mọi caller của hàm bạn sắp chạm vào; một guard trong hàm dùng chung là diff nhỏ hơn một guard trong từng caller — và nó không để các nhánh anh em hỏng.

grain / grain-review — đi thuận theo thớ

Nếu ponytail quy định viết ít đến mức nào, thì grain quy định nó đi đâu và mang hình dạng gì. Luật duy nhất của nó: tìm ví dụ hiện có gần nhất, sao chép hình dạng của nó, gọi tên thứ bạn đã sao chép. Code mới phải đọc như thể người viết file bên cạnh nó cũng viết luôn file này.

Grain cũng là nơi kiến trúc được thực thi. Backend của chúng tôi là một bố cục vertical-slice + hexagonal khá chuẩn — presentation gọi application gọi domain, infrastructure phụ thuộc vào trong, và domain không bao giờ với ngược ra ngoài. Một phát hiện như “route này bỏ qua controller của nó và gọi service trực tiếp” không phải là chuyện vặt — đó là khác biệt giữa một codebase mà agent có thể điều hướng vào tháng sau và một codebase mà nó không thể. grain-review săn đúng những thứ này: helper bị trùng lặp, vượt qua ranh giới tầng, thò tay vào nội bộ của feature khác, một quy ước thứ hai cho việc vốn đã làm theo một cách.

review-proof — giành lấy phát hiện của mình

Review code AI tiêu chuẩn tạo ra những phát hiện nghe hợp lý. Một số là thật; một số là chuyện nhảm tự tin; nhìn từ bên ngoài chúng trông y hệt nhau. review-proof từ chối tin chính mình: nó review, rồi buộc mọi phát hiện về tính đúng đắn phải tái hiện thành một test thất bại thật hoặc một bản repro chạy được, và loại bỏ mọi thứ không làm được điều đó. Nó kết thúc bằng một lượt grain-review để kiểm tra độ khớp về cấu trúc. Thứ được ship ra là một bản review mà mỗi mục đều đi kèm bằng chứng chạy được — không phải cảm giác.

Điều chúng ta nên thấy nếu cách này hiệu quả

  • H1 — Việc xoá xuất hiện trong các PR tính năng. Nếu ponytail + grain đang hoạt động, thêm một tính năng thường sẽ loại bỏ cấu trúc ròng, chứ không chỉ thêm vào.
  • H2 — Bản sửa chỉ đến một lần, ở đúng nơi dùng chung. Sửa tận gốc nên gom nhiều điểm gọi về một guard, chứ không rắc guard cho từng caller.
  • H3 — Review bắt được những đứt gãy hợp đồng mà con người lướt qua. Review có bằng chứng phải làm lộ ra những bug mà kiểu review “trông ổn đấy” bỏ sót — đặc biệt là loại âm thầm.

Phần 2 · VẬY THÌ SAO — năm PR, mỗi cái một bản năng

Năm thay đổi thật trong lõi agent của chúng tôi. Chúng tôi cố ý giữ phần nội bộ mơ hồ — repo là riêng tư — nhưng các con số được tính từ chính những diff thật, và bài học mới là phần chuyển giao được.

Trừ bớt: một guard cho cả chục điểm fetch

Một họ các bước flow fetch URL do người dùng cung cấp mà không có kiểm tra an toàn phân giải DNS, nên một hostname phân giải được thành địa chỉ nội bộ có thể chạm tới metadata cloud từ backend. Cách sửa hấp dẫn là thêm một kiểm tra tại từng điểm fetch trong số hơn chục điểm. Câu trả lời của Ponytail thì ngược lại: một helper fetch dùng chung, có guard, dùng ở mọi nơi — và chúng tôi đã xoá một bảng mạng riêng tự chế mà một mánh wildcard-DNS đi xuyên qua thẳng, cộng thêm ba vòng lặp redirect bị trùng lặp và một đoạn kiểm tra mạng bị copy-paste vào vài file.

Diff đã thêm helper dùng chung và xoá ~90 dòng trùng lặp trên 22 file. Việc xoá đó chính là câu chuyện: đây là H2 bằng xương bằng thịt — bản sửa tận gốc đã đưa mọi caller qua một guard thay vì vá đúng nhánh mà ticket nêu tên.

Hơn chục điểm fetch URL người dùng, mỗi điểm có kiểm tra riêng có thể bị vượt qua, đổ về một helper fetch dùng chung có guard

Bản sửa tận gốc: hơn chục điểm fetch tuỳ tiện thu về một helper dùng chung có guard. Cách sửa lười biếng và cách sửa an toàn là cùng một cách.

Trừ bớt + chứng minh: một guard hình dạng, được chứng minh trên runtime thật

Runtime worker của chúng tôi lưu kết quả của một lần chạy dưới dạng chuỗi đã tuần tự hoá, đôi khi được bọc trong một list một phần tử. Những consumer kỳ vọng một object thuần đã crash vì nó. Cách sửa sai là rải các kiểm tra kiểu phòng thủ ở từng consumer. Thay vào đó: một helper chuẩn hoá hình dạng nhỏ, và mọi consumer được trỏ lại về nó — một phép gần như thuần thêm một guard thay thế N guard tuỳ tiện mà một agent kém kỷ luật hơn hẳn đã viết (+558 / −24, 7 file).

Phần khiến đây là một câu chuyện review-proof là bài test. Thay vì khẳng định dựa trên một hình dạng tuần tự hoá được giả định, chúng tôi chạy một workflow thật qua runtime và storage thật — không mock — và chứng minh hình dạng mà runtime thực sự lưu. Câu hỏi mở trong ticket đúng nghĩa là runtime tuần tự hoá cái gì ở đây? — và câu trả lời trung thực duy nhất là câu trả lời bạn có thể chạy được.

Chứng minh: review bắt được một đứt gãy hợp đồng âm thầm

Tính năng: một model dự phòng theo từng node, để một flow bị ghim vào một provider có thể suy giảm một cách duyên dáng trong lúc provider gặp sự cố. Bản cắt đầu tiên hiển nhiên là bọc model trong combinator dự phòng có sẵn của framework.

Nó chạy được ở happy path. Nhưng nó cũng âm thầm tắt hai tính năng của provider vốn phụ thuộc vào kiểu model cụ thể qua isinstance — một object bị bọc không còn là kiểu đó nữa, nên cả structured output gốc lẫn prompt caching đều lặng lẽ bị tắt. Không lỗi. Không có test happy-path nào thất bại. Đúng kiểu thứ mà con người review sẽ lướt qua và một agent “trông ổn đấy” sẽ duyệt.

review-proof đã không duyệt nó — nó tái hiện cả hai đứt gãy thành những test thất bại, và điều đó buộc phải thiết kế lại: áp dụng fallback ở tầng thấp hơn, tại thời điểm thực thi, để mọi model vẫn giữ kiểu cụ thể của nó và các tính năng phụ thuộc kiểu tiếp tục hoạt động. H3, được xác nhận dứt khoát: bằng chứng đã bắt được một bug mà chỉ dựa vào vẻ hợp lý thì đã ship ra.

Căn chỉnh: một picker, không phải hai bản sao

Thêm một kênh thông báo thứ hai nghĩa là UI chọn kênh giờ có hai chỗ ở — một wizard tạo mới và một modal cài đặt. Nước đi mặc định của agent là thêm nhánh mới vào cả hai, làm lớn lên hai bản sao của cùng một state machine. grain nói: đã có một cách để thứ này được tạo hình rồi — hãy làm nó dùng chung. Chúng tôi tách ra một hook picker duy nhất và cho cả hai màn hình chạy qua nó, gắn kênh mới vào union cấu hình hiện có, và tái sử dụng dịch vụ email hiện có thay vì thêm cỗ máy gửi mới.

+1.735 / −340 trên 24 file. Một tính năng cỡ này mà xoá đi 340 dòng chính là H1 hiện hình: việc gộp hai bản sao picker thành một đã trả tiền cho một phần đáng kể của kênh mới.

Căn chỉnh + chứng minh: dùng lại endpoint, phản chiếu đường đi

Cho phép người dùng đính kèm file khi tạo issue lẽ ra có thể đồng nghĩa với một endpoint upload mới, một cơ chế dispatch mới, một đường prompt mới. grain đã đưa gần như toàn bộ việc đó lên đường ray sẵn có: endpoint upload hiện có, một danh sách chấp nhận ở frontend phản chiếu chính xác danh sách trắng của backend, và một bản sửa deferred-dispatch dùng lại cơ chế eligibility mà công việc đã lên lịch vốn đã dùng. Nó ship cùng những bản sửa có bằng chứng, bao gồm một bug cô lập giao dịch thật sự có thể làm mất dữ liệu khi upload song song. +1.776 / −218 trên 27 file — một tính năng lớn mà vẫn tìm ra 218 dòng để xoá bằng cách nhập vào các slice sẵn có thay vì phát minh ra cái mới.

Bảng điểm

Bảng điểm của năm PR Tier-3 hiển thị số dòng thêm và xoá theo từng PR, kèm bản năng định hình mỗi PR: trừ bớt, căn chỉnh, hay chứng minh

Năm PR Tier-3: mỗi cái thêm gì, xoá gì, và bản năng đằng sau nó.

Năm PR tính năng và sửa lỗi trên lõi agent đã xoá 821 dòng — không phải bằng cách làm ít hơn, mà bằng cách từ chối sao chép thứ đã tồn tại và từ chối giữ lại những bản vá triệu chứng mà một bản sửa tận gốc đã khiến thành thừa.

Công bố đầy đủ

  • Đây là kỹ năng, không phải một nghiên cứu. Đây không phải một thí nghiệm có đối chứng với một nhóm baseline; đây là năm PR thật nơi kỷ luật này được áp dụng. Phản thực tế — thứ mà cùng agent đó sẽ ship nếu không có các kỹ năng — là một ước lượng có cơ sở, không phải một phép đo. Câu chuyện đứt gãy hợp đồng âm thầm là bằng chứng sạch sẽ nhất, vì chúng tôi đã tận mắt thấy review tái hiện bug đó trước khi thiết kế lại.
  • Số dòng chỉ là đại diện. Con số −821 là số thật lấy từ diff, nhưng “số dòng bị xoá” đại diện cho “phần trùng lặp và tính linh hoạt chết đã tránh được.” Một kỹ năng thuyết phục được agent đừng viết 200 dòng suy đoán thì không để lại dấu vết gì trong diff — những thắng lợi lớn nhất ở đây là vô hình.
  • Tiết kiệm token chỉ mang tính định hướng. Diff nhỏ hơn, ít file bị chạm hơn, và cấu trúc được tái sử dụng nghĩa là tốn ít token hơn để sinh, review và review lại — nhưng chúng tôi không đưa ra một con số chênh lệch token cứng cho từng PR.
  • ponytail không phải của chúng tôi. Đây là kỹ năng mã nguồn mở của Dietrich Gebert ; chúng tôi đã tuỳ biến nó. grain và review-proof là của chúng tôi, xây theo cùng tinh thần. Cả ba chạy như những kỹ năng dự án cục bộ trên Claude Code — prompt-và-quy trình, không phải một binary bạn cài đặt.
FlowHunt Logo

Sẵn sàng phát triển doanh nghiệp của bạn?

Bắt đầu dùng thử miễn phí ngay hôm nay và xem kết quả trong vài ngày.

Phần 3 · GIỜ THÌ SAO

Đã ship gì. Ba kỹ năng có thể ghép nối. ponytail và grain chạy trong lúc viết; ponytail-review, grain-review, và review-proof chạy trên một diff. review-proof nối tiếp grain-review như pha cuối của nó, nên một bản review đã được chứng minh cũng được miễn phí một lượt kiểm tra cấu trúc.

Việc bạn có thể làm ngay thứ Hai — bạn không cần đúng những kỹ năng của chúng tôi:

  1. Đưa “tái sử dụng” lên nấc hai, không phải nấc mười. Trước khi agent viết một helper, bắt nó grep tìm cái đã có. “Nó đã có trong codebase này chưa?” phải nằm trên “mình có thể viết nó gọn gàng không?”
  2. Sửa tận gốc, chứng minh bằng danh sách caller. Yêu cầu mọi bản sửa bug phải nêu tên mọi caller của hàm bị chạm. Một guard trong hàm dùng chung thắng một guard cho mỗi caller — và đó là một diff nhỏ hơn.
  3. Cho code mới một người anh em gần nhất để sao chép. “Tìm ví dụ hiện có gần nhất và phản chiếu hình dạng của nó” biến kiến trúc từ một tài liệu không ai đọc thành một mặc định mà agent tuân theo.
  4. Thực thi ranh giới tầng trong CI, không phải bằng cảm giác. Một ratchet chỉ thất bại khi có import xuyên tầng mới cho phép bạn giữ vững ranh giới mà không phải đun sôi cả đại dương nợ hiện có.
  5. Đừng ship một phát hiện bạn không thể tái hiện. Bắt agent viết test thất bại trước. Nếu nó không làm bug tái hiện được, thì đó không phải bug — đó là một ý kiến. Bỏ nó đi.
  6. Để lượt review được xoá. Hướng một lượt review riêng vào chuyện làm quá kỹ thuật — “chúng ta cắt được gì” — tách khỏi tính đúng đắn. Kết cục tốt nhất của một diff là nó ngắn hơn.

Hãy thử, đặc biệt nếu nó không hiệu quả với bạn. Nếu bạn chạy agent lập trình trên một codebase thật, entropy đã đang diễn ra rồi — bạn chỉ chưa đo nó thôi. Biến một trong những thứ này thành một lượt review cho mười PR agent tiếp theo của bạn và đếm số dòng bị xoá. Nếu trừ-bớt-căn-chỉnh-chứng-minh không làm diff của bạn nhỏ đi hay bắt được một đứt gãy âm thầm, chúng tôi thực sự muốn nghe. Đặc biệt hoan nghênh những chỉnh sửa và phản ví dụ.

Agent thì thêm vào. Việc của bạn là bắt agent giành lấy từng dòng.

Câu hỏi thường gặp

Štefan là một kỹ sư AI và phần mềm đang xây dựng FlowHunt. Ngoài sản phẩm itself, anh thiết kế các quy trình kỹ thuật phần mềm agentic cho các nhà phát triển giúp giảm chi phí phát triển đồng thời nâng cao chất lượng mã.

Štefan Moravík
Štefan Moravík
Kỹ sư AI & Phần mềm

Xây dựng quy trình agent tự hoàn vốn

FlowHunt giúp các đội kỹ thuật thiết kế quy trình agentic, cổng review và tích hợp MCP để cắt giảm chi phí phát triển mà vẫn nâng cao chất lượng code.