Một AI agent có thể viết ra 50 test case trong 30 giây — nhưng nếu bạn không có strategy, bạn chỉ đang tạo ra 50 cách khác nhau để tự lừa dối bản thân rằng hệ thống đã an toàn.
Vì sao Test Strategy phải đi trước
Có một phản xạ rất dễ mắc phải khi làm việc với AI coding agent: viết xong một feature, mở Cursor hoặc Claude Code lên, gõ "viết unit test cho file này giúp tôi", rồi ngồi nhìn hàng chục test case xuất hiện trong vài giây. Cảm giác năng suất tăng vọt. Nhưng đây chính là cái bẫy lớn nhất của testing trong thời đại agentic engineering.
Vấn đề không nằm ở chỗ AI viết test sai cú pháp — thực ra AI hiện tại viết test chạy được rất tốt. Vấn đề là AI không biết bạn đang tối ưu cho cái gì. Nó không biết rằng trong hệ thống coupon của bạn, một bug ở logic tính discount có thể khiến công ty mất hàng chục nghìn đô trong một buổi flash sale, trong khi một bug ở màn hình hiển thị lịch sử đơn hàng chỉ gây khó chịu nhẹ cho vài khách hàng. Không có strategy, AI sẽ generate test theo nguyên tắc "phủ hết các nhánh code (branch) mà nó nhìn thấy", chứ không theo nguyên tắc "bảo vệ những gì quan trọng nhất".
Hậu quả thực tế mà nhiều team gặp phải: sau vài tháng dùng AI để "tăng tốc viết test", coverage report cho ra chỉ số đẹp — 85%, 90% — nhưng production vẫn bị vỡ trận ở đúng những chỗ nhạy cảm nhất (double-charge trong payment webhook, coupon áp dụng sai rule stacking, race condition trong rate limiter). Lý do: bộ test đó rộng nhưng nông. Nó test được nhiều dòng code nhưng không test đúng kịch bản nghiệp vụ nguy hiểm.
Chi phí sửa một test strategy sai lệch muộn còn tốn kém hơn chi phí sửa code sai. Code sai thì bạn phát hiện qua bug report, sửa, deploy lại. Nhưng một test suite "giả vờ an toàn" (false confidence) thì âm thầm ăn mòn niềm tin của cả team vào CI pipeline — đến khi một incident lớn xảy ra dù "tất cả test đều xanh", người ta mới bắt đầu nghi ngờ và phải viết lại gần như toàn bộ test suite từ đầu, lúc đó chi phí đã nhân lên gấp nhiều lần.
Test strategy, nói đơn giản, là văn bản trả lời câu hỏi: "Test đến mức nào thì gọi là đủ, cho từng phần của hệ thống?" Nó định nghĩa "done" cho việc testing — thứ mà nếu không viết ra, mỗi kỹ sư (và mỗi AI agent) sẽ tự hiểu theo một cách khác nhau.
Mẹo: Trước khi giao bất kỳ task viết test nào cho AI agent, hãy dán trước một đoạn "testing charter" ngắn (2-3 câu) vào đầu prompt hoặc vào file CLAUDE.md/AGENTS.md của project — ví dụ "Ưu tiên test các luồng liên quan đến tiền và dữ liệu khách hàng ở mức exhaustive, các luồng UI hiển thị chỉ cần smoke test". Việc này định hướng agent ngay từ đầu thay vì để nó tự đoán.
Ưu tiên theo rủi ro: Bug ở đâu tốn kém nhất
Risk-based testing không phải khái niệm mới, nhưng nó trở nên quan trọng hơn bao giờ hết khi AI có thể sinh test với tốc độ gần như vô hạn — vì lúc này, thứ khan hiếm không còn là "khả năng viết test" mà là "khả năng biết nên viết test cho cái gì trước".
Công thức kinh điển để xếp hạng rủi ro:
Risk Score = Business Impact × Likelihood × Blast Radius
- Business Impact: nếu bug này xảy ra, thiệt hại là gì — tiền, uy tín, pháp lý (compliance), hay chỉ là trải nghiệm không hoàn hảo?
- Likelihood: khả năng xảy ra bug ở khu vực code này cao hay thấp? Code cũ, ổn định, ít đổi thì likelihood thấp hơn code mới, nhiều nhánh điều kiện, nhiều người cùng sửa.
- Blast Radius: nếu lỗi xảy ra, nó ảnh hưởng một khách hàng, một tenant, hay toàn bộ hệ thống?
Áp dụng vào một hệ thống coupon/discount engine điển hình của e-commerce, ta có thể xếp hạng như sau:
| Khu vực | Business Impact | Likelihood | Blast Radius | Ưu tiên |
|---|---|---|---|---|
| Tính toán số tiền giảm giá (percentage/fixed, stacking rules) | Rất cao (mất tiền trực tiếp) | Trung bình-cao (nhiều rule phức tạp) | Cao (áp dụng cho mọi đơn hàng) | P0 |
| Kiểm tra điều kiện hết hạn (expiry) của coupon | Cao (khách dùng coupon hết hạn = mất tiền, hoặc coupon còn hạn bị từ chối = mất khách) | Trung bình | Trung bình | P0 |
| Idempotency khi retry thanh toán (payment webhook) | Cực cao (double-charge, compliance) | Thấp nhưng hậu quả nặng | Cao | P0 |
| Giao diện hiển thị mã coupon đã áp dụng trên UI | Thấp | Trung bình | Thấp (một session) | P2 |
| Log định dạng cho admin dashboard | Rất thấp | Thấp | Thấp | P3 |
Nhìn bảng này, một điều rất rõ: nơi liên quan đến tiền, bảo mật, và compliance luôn nằm ở nhóm rủi ro cao nhất, bất kể độ phức tạp code trông "đơn giản" đến đâu. Nhiều kỹ sư (và AI) có xu hướng đánh giá độ ưu tiên test theo độ phức tạp cyclomatic của hàm, nhưng risk-based prioritization bắt bạn phải hỏi ngược lại: "Nếu hàm này sai, ai trả giá, và trả giá bao nhiêu?"
Khi giao việc cho AI agent, bảng risk matrix này chính là ngữ cảnh (context) sống còn. Một prompt tốt sẽ luôn kèm risk tier của module đang được test, để agent phân bổ effort — số lượng edge case, số lượng test — tương xứng.
Mẹo: Lưu risk matrix của dự án thành một file
TESTING_RISK_MATRIX.mdngay trong repo, cập nhật mỗi quý. Khi prompt AI viết test cho một module, luôn trỏ agent đọc file này trước — nó giúp AI tự quyết định "module này cần test exhaustive hay chỉ cần happy-path" mà không cần bạn nhắc lại mỗi lần.
Test Pyramid trong bối cảnh có AI hỗ trợ
Test pyramid — nhiều unit test ở đáy, ít integration test ở giữa, rất ít end-to-end (e2e) test ở đỉnh — vẫn là mô hình đúng về mặt kiến trúc chi phí: unit test chạy nhanh, rẻ, cô lập; e2e test chạy chậm, đắt, dễ flaky. Nhưng AI đang làm biến dạng động lực xây dựng pyramid này theo hai hướng ngược nhau, và cả hai đều nguy hiểm.
Hướng thứ nhất — phình to đáy pyramid một cách giả tạo. Vì AI sinh unit test cực nhanh, team dễ rơi vào việc yêu cầu AI "viết thêm test" liên tục cho cùng một hàm, dẫn đến hàng trăm unit test na ná nhau — test cùng một nhánh logic với dữ liệu đầu vào chỉ khác nhau ở tên biến. Coverage % tăng, nhưng độ tin cậy thực sự (mutation score, khả năng bắt bug thật) không tăng tương ứng. Đây là dạng "false productivity": nhiều dòng test hơn không đồng nghĩa với an toàn hơn.
Hướng thứ hai — anti-pattern ice-cream-cone ngược lại. Vì unit test do AI viết đôi khi mock quá nhiều (mock hết database, mock hết service khác), team có cảm giác an tâm giả tạo rằng "đã có unit test rồi", nên bỏ qua việc đầu tư vào integration test và contract test — nơi thực sự phát hiện ra các vấn đề như sai kiểu dữ liệu giữa service, sai version API, hay race condition thực tế. Kết quả: pyramid bị biến dạng thành hình đồng hồ cát — nhiều unit test, nhiều e2e test (vì QA cảm thấy bất an nên viết thêm e2e), nhưng thiếu hẳn tầng giữa integration/contract.
Trong một hệ thống có nhiều service giao tiếp qua API — ví dụ order-service gọi coupon-service để validate mã giảm giá — chính tầng integration/contract test (dùng Pact, hoặc contract test tự viết) là nơi bắt được lỗi kiểu "coupon-service đổi field discount_amount thành discountAmount mà không ai biết", một lỗi mà unit test (mock hết) và e2e test (chạy quá chậm nên ít khi chạy full) đều dễ bỏ lọt.
Nguyên tắc thực dụng khi làm việc với AI agent ở đây: đừng để AI tự quyết định tỷ lệ pyramid. Bạn, kỹ sư senior, là người quyết định layer nào cần đầu tư bao nhiêu effort; AI chỉ là công cụ thực thi nhanh trong layer đó. Khi prompt agent viết test, luôn chỉ rõ layer:
Viết integration test cho endpoint POST /orders/{id}/apply-coupon.
Test phải gọi thật CouponService (không mock), chỉ mock PaymentGateway.
Bao gồm case: coupon hợp lệ, coupon hết hạn, coupon đã dùng hết lượt,
stacking hai coupon không được phép cộng dồn.
Không viết thêm unit test cho logic tính discount (đã có ở discount.test.ts).
Prompt như trên buộc AI phải làm việc trong đúng layer bạn muốn, tránh nó "tiện tay" viết thêm hàng chục unit test trùng lặp.
Mẹo: Nếu review PR thấy AI sinh ra > 15 unit test cho một hàm dưới 30 dòng code, đó là dấu hiệu cảnh báo (code smell) — dừng lại hỏi "những test này có thực sự khác nhau về assertion, hay chỉ khác input value?" Thường 5-7 test case được thiết kế tốt (theo boundary/equivalence class) đã đủ hơn 20 test case rải rác ngẫu nhiên.
Dùng AI để tìm điểm mù trong coverage
Đây là chỗ AI thực sự tỏa sáng — không phải ở việc viết test, mà ở việc đọc code và test hiện có rồi chỉ ra khoảng trống. Một kỹ sư đọc diff 300 dòng để tìm edge case dễ bỏ sót; AI đọc rất kỹ và không mệt.
Cách làm hiệu quả nhất: cho AI agent xem đồng thời (1) file code, (2) file test hiện tại, và (3) coverage report (lcov hoặc istanbul JSON) nếu có, rồi hỏi nó liệt kê các nhánh/case chưa được test — chứ không hỏi nó viết test ngay lập tức. Tách bước "phân tích" ra khỏi bước "sinh code" giúp bạn kiểm soát chất lượng tốt hơn nhiều.
Ví dụ prompt thực tế cho tính năng áp coupon:
Đây là source code của hàm applyCoupon (src/coupon/apply-coupon.ts)
và test hiện tại (src/coupon/apply-coupon.test.ts).
Tôi cũng đính kèm coverage report dạng lcov (coverage/lcov.info)
cho thấy dòng 42-48 và 67-71 chưa được cover.
Nhiệm vụ của bạn:
1. Liệt kê tất cả các nhánh logic (branch), boundary condition,
và error path trong applyCoupon mà KHÔNG có test tương ứng.
2. Với mỗi điểm mù, giải thích ngắn gọn hậu quả nghiệp vụ nếu bug
xảy ra ở đó (ví dụ: "nếu dòng 45 sai, khách có thể áp 2 coupon
percentage cùng lúc dẫn tới giảm giá âm").
3. KHÔNG viết test code ở bước này. Chỉ liệt kê dạng bảng:
| Vị trí | Mô tả case chưa test | Mức độ rủi ro (P0/P1/P2) |
4. Ưu tiên các case liên quan tới: số tiền âm, overflow số lượng
coupon dùng, coupon hết hạn theo timezone, concurrent request
dùng cùng 1 coupon (race condition).
Sau khi có bảng điểm mù, bạn review, gạch bỏ những case không quan trọng, bổ sung case nghiệp vụ mà AI không biết (ví dụ: quy định pháp lý riêng của thị trường VN về khuyến mãi), rồi mới yêu cầu AI viết test cho từng dòng còn lại — theo đúng thứ tự ưu tiên risk.
Một lưu ý từ kinh nghiệm thực tế: coverage report chỉ cho biết dòng nào được chạy qua, không cho biết dòng đó có được assert đúng hay không. Vì vậy đừng dừng lại ở "coverage tăng" — hãy hỏi thêm AI: "trong số các test hiện tại, test nào có assertion yếu (ví dụ chỉ check expect(result).toBeDefined())?" Đây chính là dạng test "tick-the-box" rất phổ biến khi AI generate hàng loạt mà không ai review kỹ.
Mẹo: Khi feed coverage report cho AI, luôn kèm theo cả file diff của PR (
git diff main...HEAD), không chỉ file cuối cùng — agent sẽ tập trung phân tích đúng phần code mới thay đổi thay vì lan man phân tích toàn bộ file, tiết kiệm context window đáng kể.
Viết tài liệu Test Plan cùng AI
Một test plan document tốt không phải để "làm màu" cho auditor — nó là hợp đồng giữa các bên (dev, QA, product) về việc test đến đâu là đủ, và ai chịu trách nhiệm cho phần nào. AI có thể giúp bạn draft bộ khung này rất nhanh từ một feature spec, miễn là bạn biết cấu trúc cần có.
Cấu trúc test plan tối thiểu cho một feature vừa và lớn:
- Scope — feature này bao gồm gì, không bao gồm gì (out of scope phải ghi rõ để tránh scope creep khi review).
- Risk matrix — như phần 2 đã trình bày, áp dụng cụ thể cho feature này.
- Test levels — unit/integration/contract/e2e sẽ cover phần nào, ai viết (dev tự viết unit, QA viết e2e chẳng hạn).
- Test environments — staging có mock payment gateway không, dữ liệu test lấy từ đâu, có cần môi trường riêng cho load test không.
- Exit criteria — coverage tối thiểu theo tier rủi ro (xem phần 6), số lượng bug P0/P1 mở tối đa trước khi release, mutation score tối thiểu cho module P0.
- Rollback / monitoring plan — nếu bug lọt qua production, phát hiện bằng cách nào (alert, dashboard), rollback ra sao.
Prompt mẫu để AI draft từ feature spec:
Tôi có feature spec sau (dán full nội dung feature spec vào đây):
[FEATURE SPEC: Coupon Stacking & Expiry Engine]
Hãy soạn một Test Plan Document theo cấu trúc:
1. Scope (in scope / out of scope)
2. Risk Matrix (dùng công thức Business Impact x Likelihood x Blast Radius,
xếp mỗi hạng mục vào P0/P1/P2)
3. Test Levels (unit / integration / contract / e2e) - với mỗi level
liệt kê 3-5 kịch bản tiêu biểu, không cần viết code test
4. Test Environments cần thiết
5. Exit Criteria (đề xuất coverage %, mutation score theo risk tier)
6. Rollback & Monitoring Plan
Giả định hệ thống dùng Node.js/TypeScript, Jest cho unit/integration,
Playwright cho e2e, có sẵn feature flag system để rollback nhanh.
Văn phong: rõ ràng, dạng bảng khi cần, dùng được ngay để review với
team lead và QA lead.
Điều quan trọng: bản draft AI trả về là điểm khởi đầu, không phải bản final. Bạn cần ngồi lại với QA lead và product owner để chỉnh sửa risk matrix (AI không biết được áp lực kinh doanh thực tế, ví dụ campaign sắp chạy trong 3 ngày nữa nên rủi ro launch trễ cũng phải tính vào), và chốt exit criteria dựa trên năng lực thực tế của team (không phải con số lý tưởng AI đề xuất).
Mẹo: Sau khi AI draft xong test plan, hãy nhờ chính agent đó đóng vai "reviewer khó tính" bằng cách hỏi tiếp: "Đóng vai một Staff Engineer khó tính, hãy chỉ ra 3 lỗ hổng lớn nhất trong test plan vừa soạn." Kỹ thuật self-critique này thường lòi ra những điểm yếu mà bản draft đầu tiên bỏ sót.
Mục tiêu coverage thực dụng cho code do AI sinh ra
"100% coverage" là một vanity metric ngay cả trước thời AI, nhưng nó trở nên nguy hiểm hơn hẳn khi AI tham gia viết test — vì AI rất giỏi trong việc đạt được con số coverage cao mà không cần đạt được chất lượng tương ứng. Một AI agent được yêu cầu "tăng coverage lên 90%" hoàn toàn có thể sinh ra những test kiểu:
it('should call applyCoupon without throwing', () => {
applyCoupon(order, coupon);
expect(true).toBe(true); // "test" này chỉ để dòng code được chạy qua
});
Test này làm coverage tool hài lòng — dòng code được thực thi — nhưng không assert bất kỳ điều gì về kết quả. Đây gọi là "coverage padding" hoặc assertion-free test, và nó là rủi ro thực sự khi bạn khoán chỉ tiêu coverage số cho AI mà không review kỹ.
Cách tiếp cận thực dụng hơn: đặt mục tiêu coverage theo risk tier, không phải một con số chung cho toàn bộ codebase:
| Risk Tier | Ví dụ | Line coverage mục tiêu | Ghi chú |
|---|---|---|---|
| P0 (tiền, bảo mật, compliance) | Payment webhook, discount calculation, auth | 90-95%+ và bắt buộc mutation testing | Coverage cao chưa đủ, cần mutation score tối thiểu 70-80% |
| P1 (nghiệp vụ quan trọng nhưng không trực tiếp mất tiền) | Order status workflow, notification | 75-85% | Tập trung integration test hơn unit thuần |
| P2 (UI, hiển thị, thao tác phụ) | Dashboard hiển thị, export report | 40-60% | Ưu tiên smoke test + e2e critical path |
| P3 (script nội bộ, tool dev) | Seed script, migration helper | Không bắt buộc | Review thủ công đủ |
Với các module P0, coverage % chỉ là điều kiện cần. Điều kiện đủ là mutation testing — cố tình chèn lỗi nhỏ vào code (đổi > thành >=, đổi && thành ||) rồi xem test suite có bắt được lỗi đó không (mutation score). Đây là kỹ thuật sẽ được đào sâu ở bài "Property-Based Testing và Mutation Testing" của module này — ở đây chỉ cần nắm nguyên tắc: coverage đo "code có chạy qua", mutation score đo "test có thực sự phát hiện được sai sót".
Một thực hành tốt khi review PR có test do AI sinh: chạy thử công cụ như Istanbul/nyc hoặc JaCoCo (Java), rồi mở report ở chế độ "branch coverage" thay vì chỉ "line coverage" — line coverage rất dễ đạt 90% trong khi branch (nhánh if/else, ternary) vẫn còn lỗ hổng lớn. SonarQube cũng là công cụ tốt để phát hiện những test "rỗng ruột" kiểu trên qua rule về assertion density.
Mẹo: Thêm một lint rule hoặc custom SonarQube rule cấm test không có
expect/assertnào bên trong block — chặn ngay tại CI, đừng để việc phát hiện assertion-free test phụ thuộc vào review thủ công của con người, vì con người cũng dễ lướt qua khi thấy coverage % đẹp.
Thực hành: Xây dựng Test Strategy Document từ một Feature Spec
Hãy đi qua một ví dụ đầy đủ, từ feature spec đến test strategy document hoàn chỉnh, dùng chính coupon engine làm ví dụ xuyên suốt.
Bước 1 — Feature spec (giả định đã có sẵn):
Feature: Coupon Application with Stacking & Expiry
Cho phép khách hàng áp dụng một hoặc nhiều mã coupon vào đơn hàng. Coupon có hai loại: percentage (giảm %) và fixed (giảm số tiền cố định). Business rule: percentage coupon không được stack với percentage coupon khác; fixed coupon có thể stack tối đa 2 mã cùng loại. Mỗi coupon có ngày hết hạn (expiry) tính theo UTC nhưng hiển thị theo timezone khách hàng. Mỗi coupon có giới hạn số lần sử dụng toàn hệ thống (usage_limit) và giới hạn theo từng khách hàng (per_user_limit).
Bước 2 — Prompt gửi cho AI agent:
Bạn là Staff QA Engineer. Dựa trên feature spec dưới đây, hãy soạn
Test Strategy Document đầy đủ.
[Dán feature spec ở trên]
Yêu cầu output gồm các phần: Scope, Risk Matrix (dùng Business Impact
x Likelihood x Blast Radius, xếp P0/P1/P2), Test Pyramid Plan (bao
nhiêu % effort cho unit/integration/e2e và vì sao), Coverage Goals
theo risk tier, danh sách Edge Cases bắt buộc phải test (đặc biệt
chú ý: race condition khi 2 request cùng dùng 1 coupon gần hết
usage_limit, sai lệch timezone quanh nửa đêm ngày expiry, thứ tự
áp dụng coupon ảnh hưởng tới kết quả tính toán), và Exit Criteria.
Không viết test code, chỉ viết document ở mức strategy.
Bước 3 — Trích đoạn AI có thể trả về (ví dụ minh hoạ):
Risk Matrix
- Logic tính discount khi stacking 2 fixed coupon — Impact: Cao, Likelihood: Cao (nhiều rule), Blast Radius: Cao → P0
- Kiểm tra usage_limit dưới điều kiện concurrent request — Impact: Cao (có thể vượt limit gây lỗ), Likelihood: Trung bình, Blast Radius: Cao → P0
- Hiển thị ngày hết hạn theo timezone khách hàng trên UI — Impact: Trung bình (gây nhầm lẫn, không mất tiền trực tiếp), Likelihood: Trung bình, Blast Radius: Thấp → P1Edge Cases bắt buộc
1. Áp 2 coupon percentage cùng lúc → phải bị từ chối với message rõ ràng.
2. Coupon hết hạn lúc 00:00 UTC ngày 10, khách ở UTC+7 áp lúc 23:59 ngày 10 (giờ địa phương) nhưng đã là 16:59 UTC ngày 10 → vẫn hợp lệ hay không, cần rule rõ ràng.
3. Hai request đồng thời cùng áp mã coupon còn đúng 1 lượt sử dụng cuối (usage_limit - current_usage = 1) → chỉ một request được thành công, request kia phải nhận lỗi rõ ràng, không được double-apply.
Bước 4 — Review và chỉnh sửa của kỹ sư: Đây là bước không thể bỏ qua. Khi review bản draft, một senior engineer trong thực tế sẽ:
- Phát hiện AI chưa tính đến trường hợp coupon bị thu hồi (revoke) giữa lúc khách đang checkout — bổ sung thêm case này vào P0.
- Điều chỉnh risk matrix: nếu campaign coupon này gắn với chiến dịch marketing lớn sắp chạy, tăng mức độ ưu tiên toàn bộ module lên, bất kể risk score kỹ thuật thuần túy.
- Chốt lại exit criteria cụ thể: "P0 modules cần mutation score ≥ 75%, đo bằng Stryker (JS) trước khi merge vào release branch."
- Giao lại đúng phần integration test cho AI viết theo layer, đúng risk tier, dùng prompt đã học ở phần 3 và 4.
Kết quả cuối cùng không phải một document AI viết ra rồi để đó, mà là một tài liệu sống — được AI tăng tốc phần soạn thảo, nhưng được con người chịu trách nhiệm về phán đoán rủi ro và quyết định cuối cùng.
Mẹo: Lưu test strategy document ngay trong repo cạnh feature spec (ví dụ
docs/test-strategy/coupon-stacking.md), không để trong Confluence hay Notion tách rời code — để lần sau prompt AI agent viết test, bạn chỉ cần trỏ nó đọc file này, đảm bảo agent luôn có đúng ngữ cảnh risk tier khi làm việc trên module đó.
Những điểm chính
- Test strategy phải có trước khi giao việc cho AI viết test — nếu không, AI sẽ tối ưu theo "phủ code" thay vì "bảo vệ giá trị nghiệp vụ quan trọng nhất".
- Dùng công thức Risk Score = Business Impact × Likelihood × Blast Radius để xếp hạng ưu tiên; luôn ưu tiên P0 cho những gì liên quan tới tiền, bảo mật, compliance.
- AI làm phình to đáy test pyramid (quá nhiều unit test trùng lặp) hoặc khiến team bỏ quên tầng integration/contract — kỹ sư phải chủ động chỉ định layer khi prompt, không để AI tự quyết.
- Dùng AI để phân tích tìm điểm mù coverage (kèm coverage report và diff) trước, tách riêng khỏi bước sinh test code, để kiểm soát chất lượng tốt hơn.
- Test plan document cần đủ 6 phần: Scope, Risk Matrix, Test Levels, Environments, Exit Criteria, Rollback/Monitoring — AI có thể draft nhanh nhưng con người phải chốt risk và exit criteria.
- 100% coverage là vanity metric nguy hiểm với AI-generated test vì dễ bị "coverage padding" (test không assertion) — đặt mục tiêu coverage theo risk tier và bổ sung mutation testing cho module P0.
- Luôn coi output AI là bản nháp cần review, không phải quyết định cuối cùng — vai trò senior engineer là phán đoán rủi ro nghiệp vụ mà AI không có ngữ cảnh để biết.