Pipeline CI/CD xanh hoàn toàn. Coverage báo cáo 94%. Code review được approve. Bạn merge với sự tự tin — rồi mười lăm phút sau, Slack bắt đầu đổ chuông.
Đây không còn là tình huống giả định nữa. Trong thế giới mà AI agent viết code ngày càng nhiều, chúng tôi đang chứng kiến một lớp bug mới xuất hiện: loại mà mọi test đều pass, nhưng bản thân các test lại sai về mặt cơ bản.
Khi Agent Tự Kiểm Tra Chính Mình
Vấn đề bắt đầu từ một điểm tưởng chừng vô hại: khi bạn yêu cầu AI agent implement một tính năng, nó không chỉ viết code — nó còn viết cả test và mock cho những dependency bên ngoài. Điều này nghe có vẻ hiệu quả, nhưng chứa đựng một rủi ro ẩn.
Mock mà agent tạo ra phản ánh giả định của nó về cách hệ thống bên ngoài hoạt động. Đây không phải là kiến thức thực tế từ tài liệu chính thức hay hành vi runtime đã quan sát — đây là suy luận từ pattern training data, được gói gọn thành code trông hoàn toàn hợp lệ.
Đây chính là verification gap: khoảng cách giữa những gì test chứng minh (tính nhất quán nội tại của code) và những gì thực sự cần thiết (tính đúng đắn so với thế giới thực).
Giải Phẫu Một Failure
Hãy xem xét một ví dụ cụ thể. Agent được yêu cầu implement một payment retry handler tích hợp với Stripe API.
# Code agent viết
class PaymentRetryHandler:
def __init__(self, stripe_client, max_retries=3):
self.stripe = stripe_client
self.max_retries = max_retries
def process_with_retry(self, payment_intent_id: str) -> dict:
for attempt in range(self.max_retries):
try:
result = self.stripe.payment_intents.confirm(payment_intent_id)
return {"status": "success", "charge_id": result.charges[0].id}
except stripe.error.CardError as e:
if attempt == self.max_retries - 1:
raise
time.sleep(2 ** attempt)
# Mock agent tự tạo cho test
class MockStripeClient:
def __init__(self):
self.payment_intents = MockPaymentIntents()
class MockPaymentIntents:
def confirm(self, payment_intent_id):
return MockPaymentIntent(
charges=[MockCharge(id="ch_test_123")]
)
Test pass 100%. Coverage đạt. Nhưng có vấn đề: Stripe API thực tế không trả về result.charges như một list trực tiếp. Nó trả về một StripeObject với thuộc tính data — result.charges.data[0].id. Agent đã mock theo giả định sai về cấu trúc response, và vì mock của nó nhất quán với code của nó, không có test nào fail.
Production gọi Stripe thật. AttributeError xảy ra ngay lập tức.
Ba Tầng Của Verification Gap
Qua nhiều lần quan sát, tôi thấy gap này thường xuất hiện ở ba dạng:
Tầng 1 — Schema drift: API thực tế có cấu trúc response khác với những gì agent giả định từ naming convention hay pattern chung. charges thay vì charges.data, items thay vì results, timestamp là string ISO thay vì Unix integer.
Tầng 2 — Error contract: Agent mock thường chỉ simulate “happy path” errors. Nhưng production services có error taxonomy phức tạp — idempotency key conflicts, partial failures, rate limit responses với retry-after headers, errors lồng trong errors.
Tầng 3 — State assumptions: Mock thường stateless, trong khi integration thực tế có state. Một Stripe PaymentIntent đã ở trạng thái requires_action không thể confirm theo cách thông thường. Agent không biết điều này; mock của nó không mô phỏng điều này.
Ba Khuyến Nghị Cho Tech Lead
1. Contract test là bắt buộc, không phải tùy chọn
Với bất kỳ external service nào, hãy có ít nhất một test layer sử dụng actual response structure. Điều này không nhất thiết phải là integration test chạy live — có thể là recorded fixtures từ API thật.
# Thay vì để agent tạo mock, record fixture từ API thật
# stripe_fixtures/payment_intent_confirm_success.json
# Sau đó load fixture đó trong test
with open("stripe_fixtures/payment_intent_confirm_success.json") as f:
fixture = json.load(f)
mock_client.payment_intents.confirm.return_value = stripe.util.convert_to_stripe_object(fixture)
Sự khác biệt quan trọng: fixture đến từ thực tế, không phải từ giả định. Agent có thể tạo loader code cho fixture — nhưng bản thân fixture phải đến từ source của sự thật.
2. Thiết lập “agent-written test” review checklist
Khi review code từ AI agent, hãy đặc biệt chú ý đến mock và stub. Tạo checklist nhanh:
- Mock này dựa trên tài liệu chính thức hay convention suy đoán?
- Error case nào bị bỏ qua trong mock?
- Có state transitions nào không được mô phỏng?
- Schema của mock có được validate với actual API response không?
Điều này không có nghĩa là reject tất cả agent-generated test. Nó có nghĩa là treat chúng như draft cần được verify bởi người có kiến thức về service thực tế.
3. Smoke test layer trong CI/CD
Thêm một stage trong pipeline chạy một tập hợp nhỏ integration test thật — có thể chỉ 5-10 test cases — với sandbox environment của service thực tế. Stage này chạy sau unit test, trước deploy.
Chi phí: vài phút thêm vào pipeline. Lợi ích: phát hiện verification gap trước khi production.
Với Stripe, có Stripe test mode. Với AWS, có localstack hoặc actual sandbox account. Với internal services, có staging environment. Hầu hết modern services đều có cơ chế test không ảnh hưởng production data.
Verification Gap Là Structural, Không Phải AI Bug
Điều quan trọng cần hiểu: đây không phải là lỗi của AI agent. Agent không “sai” khi tạo mock — nó đang làm chính xác những gì được yêu cầu theo cách hợp lý nhất với thông tin nó có.
Vấn đề là structural: chúng ta đã có workflow testing được thiết kế cho con người viết mock dựa trên kiến thức thực tế về service. Khi agent thay thế con người trong bước đó, kiến thức thực tế đó không tự động được chuyển giao.
Tech Lead cần nhận ra rằng “AI viết test” không đồng nghĩa với “test được verify”. Hai điều đó là khác nhau, và khoảng cách giữa chúng chính là nơi production incident sinh ra.
Kết Luận
Pipeline xanh là cần thiết nhưng chưa đủ. Trong thời đại AI-assisted development, bạn cần hỏi thêm một câu hỏi trước mỗi merge: Những test này đang chứng minh điều gì, và chúng biết điều đó từ đâu?
Nếu câu trả lời là “từ giả định của agent” — bạn có một verification gap cần đóng trước khi code chạm production.