·

Tiếng Việt: The dual-context model

The dual-context model

Khi bạn nhờ AI viết test case, phân tích risk, hay khoanh vùng regression, chất lượng output phụ thuộc gần như hoàn toàn vào loại context (ngữ cảnh) bạn đưa vào — không phải vào việc bạn dùng model nào hay prompt "thần chú" nào. Sau nhiều lứa dự án dùng agentic testing trong thực tế, tôi rút ra một quy tắc đơn giản: mọi task QA có AI hỗ trợ đều xoay quanh hai loại context, và bài này sẽ giúp bạn tách bạch, kết hợp, và xử lý khi thiếu một trong hai.

Hai loại context nào mà QA engineer nào cũng cần khi làm agentic testing?

Nếu bạn từng thấy AI sinh ra một bộ test case "đúng chuẩn sách giáo khoa" nhưng chẳng liên quan gì đến thứ team vừa merge tuần này, thì bạn đã chạm vào đúng vấn đề mà mô hình Dual-Context giải quyết. AI không tự biết "cái gì nên xảy ra" và "cái gì vừa thay đổi" — bạn phải cung cấp cả hai, và cung cấp đúng cách.

Hai loại context

Có đúng hai trục context mà mọi task testing dựa vào AI cần phối hợp:

  • Spec Context (context về đặc tả/yêu cầu): Trả lời câu hỏi "Hệ thống được kỳ vọng hoạt động như thế nào?" — gồm PRD, user story, acceptance criteria, OpenAPI spec, business rule, quy định compliance...
  • Code-Change Context (context về thay đổi code): Trả lời câu hỏi "Cái gì vừa thực sự thay đổi?" — gồm git diff, PR description, commit message, danh sách file bị ảnh hưởng.

Hai trục này độc lập nhưng bổ trợ cho nhau. Spec context là "đích" (target behavior), code-change context là "vector di chuyển" (cái gì đang thay đổi hướng về đích đó, hoặc lệch khỏi đích đó).

Hình dung mô hình Dual-Context

Cách tôi hay vẽ lên bảng khi training team mới:

SPEC CONTEXT                     CODE-CHANGE CONTEXT
(Behavior kỳ vọng)                (Thay đổi thực tế)
      |                                  |
      |            overlap              |
      +--------- TEST FOCUS ZONE -------+
      |    (nơi AI nên tập trung test)   |
      |                                  |
  Spec-only zone                  Diff-only zone
  (chưa code / spec              (thay đổi không
   chưa được implement)           nằm trong spec —
                                   rủi ro scope creep)

Vùng giao nhau (overlap) chính là nơi bạn muốn AI đầu tư nhiều nhất effort test: đó là hành vi vừa được thay đổi VÀ có kỳ vọng rõ ràng để đối chiếu đúng/sai. Hai vùng lệch ra ngoài — spec chưa được code, hoặc code làm việc gì đó ngoài spec — chính là nơi gap (khoảng trống) hay xuất hiện, và tôi sẽ nói kỹ ở phần sau.

Vì sao cần cả hai loại context

Thử nghĩ hai tình huống cực đoan:

  • Chỉ có spec context, không có diff: AI sẽ generate một bộ test case rất đầy đủ về mặt lý thuyết, nhưng dàn trải — nó test toàn bộ feature như thể mọi thứ đều mới, trong khi thực ra 90% hành vi không đổi so với sprint trước. Bạn tốn effort review một núi test case không liên quan đến rủi ro thực tế của release này.
  • Chỉ có diff, không có spec: AI thấy được "cái gì đổi" nhưng không biết "đổi vậy có đúng không". Nó dễ rơi vào việc mô tả lại behavior hiện tại của code như một sự thật hiển nhiên, kể cả khi đó chính là bug. Tôi từng thấy AI "test case hoá" một lỗi tính phí sai chỉ vì code diff làm vậy và không có spec nào để đối chiếu.

Kết hợp cả hai, AI mới có đủ dữ liệu để trả lời câu hỏi thực sự quan trọng: "phần vừa thay đổi này có đang thoả yêu cầu không, và thay đổi đó kéo theo rủi ro gì ở những phần liên quan?"

Mẹo: Đừng nhồi toàn bộ PRD 20 trang vào context chỉ vì "có sẵn". Trích đúng đoạn acceptance criteria liên quan đến vùng code vừa đổi. Context càng dài mà càng ít liên quan, model càng dễ bị loãng chú ý (attention) và bỏ sót đúng chỗ cần soi kỹ nhất.

Spec và Requirements Context định nghĩa hành vi mong đợi (expected behavior) như thế nào?

Spec context là "nguồn sự thật" (source of truth) để AI biết đâu là đúng, đâu là sai. Nhưng không phải tài liệu nào gắn mác "spec" cũng đủ chất lượng để làm context tốt — đây là phần nhiều QA bỏ qua và trả giá bằng test case sai lệch.

Những gì được tính là Spec Context

Trong thực tế, spec context của bạn thường là tổ hợp của nhiều nguồn, không chỉ một tài liệu PRD duy nhất:

  • User story kèm acceptance criteria (lý tưởng nhất viết theo dạng Given-When-Then).
  • PRD hoặc feature brief từ Product Manager.
  • OpenAPI/Swagger spec, GraphQL schema — với API, đây là nguồn chính xác nhất.
  • Business rule document (ví dụ: quy tắc tính phí, quy tắc phân quyền, ngưỡng compliance).
  • Design/UX flow (Figma annotation, flow diagram) — quan trọng với test UI/UX.
  • Bug ticket cũ đã được confirm là "hành vi đúng" (dùng như regression guard, tránh lặp lại bug đã fix).

Mẹo nhỏ về thứ tự ưu tiên: khi các nguồn này mâu thuẫn nhau (rất thường xảy ra — PRD nói một kiểu, code comment nói kiểu khác), luôn ưu tiên acceptance criteria đã được confirm gần nhất trong ticket, rồi mới đến PRD tổng, rồi mới đến "tacit knowledge" truyền miệng.

Biến Spec Context thành dạng máy đọc được

PRD viết theo văn phong tự do, nhiều đoạn văn dài, là "món ăn khó nhá" cho LLM — model dễ tóm sai ý hoặc bỏ sót chi tiết nằm giữa đoạn văn. Cách tôi hay làm trước khi đưa spec vào prompt:

  • Chuyển acceptance criteria dạng văn xuôi thành Gherkin (Given-When-Then) hoặc bullet có cấu trúc rõ ràng.
  • Trích riêng bảng business rule (nếu có threshold, enum, formula) thành table markdown thay vì để nguyên trong đoạn văn.
  • Với API, luôn ưu tiên đưa schema (JSON Schema/OpenAPI) thay vì mô tả bằng lời — schema có type, required, enum rất tường minh, ít chỗ cho model "đoán".

Ví dụ chuyển đổi nhanh: câu PRD "Người dùng có thể nhập mã giảm giá, nếu mã hết hạn hoặc không tồn tại thì phải báo lỗi rõ ràng và không được trừ tiền" trở thành:

GIVEN người dùng đang ở bước checkout
WHEN người dùng nhập promo code hợp lệ nhưng đã hết hạn
THEN hệ thống trả lỗi "Mã giảm giá đã hết hạn"
AND không có giao dịch trừ tiền nào được tạo
AND giỏ hàng giữ nguyên tổng tiền trước khi nhập mã

Dạng này AI xử lý chính xác hơn nhiều so với việc tự parse câu văn gốc.

Versioning Spec Context

Một lỗi rất phổ biến: bạn feed cho AI spec "hiện tại" trong khi code branch đang test lại được viết dựa trên spec version cũ (hoặc ngược lại, spec đã update cho sprint sau nhưng code chưa catch up). Kết quả là AI "phán" sai vì so sánh lệch version.

Cách xử lý:

  • Luôn ghi rõ trong prompt ngày cập nhật cuối của spec, hoặc version/tag của ticket (ví dụ: "Acceptance criteria này là bản cập nhật ngày 2026-07-05, sau khi PM đổi rule giảm giá").
  • Nếu có thể, lưu spec dưới dạng file trong repo (spec-as-code), version cùng với code — nhánh nào, spec nào theo nhánh đó, không còn lệch pha.
  • Với feature đang thay đổi nhiều lần trong sprint, luôn hỏi lại PM một câu chốt: "đây có phải bản mới nhất chưa" trước khi build test case hàng loạt — tốn 30 giây hỏi còn hơn build sai cả bộ test.

Sử dụng OpenAPI Spec làm Context chính xác cao

Với API testing, OpenAPI/Swagger spec là loại spec context "sạch" nhất bạn có thể đưa cho AI — vì nó có type, required field, enum, response code tường minh, không mập mờ như văn xuôi.

Ví dụ prompt tận dụng OpenAPI schema:

Đây là OpenAPI schema cho endpoint POST /orders:

<paste schema YAML/JSON>

Dựa trên schema này, generate test case cho:
1. Mọi field required bị thiếu (mỗi field một test case riêng).
2. Field có enum — test với giá trị hợp lệ, giá trị ngoài enum, giá trị sai kiểu (case-sensitivity).
3. Field numeric — test boundary min/max theo constraint khai báo (nếu có minimum/maximum).
4. Mọi response code khai báo trong spec (200, 400, 401, 404, 409...) — mô tả điều kiện để trigger từng code.

Output dạng bảng: ID | Field/Scenario | Input | Expected Status | Expected Response.

Vì schema đã tường minh về constraint, output AI trả về thường bám sát thực tế hơn nhiều so với khi bạn chỉ mô tả API bằng lời.

Mẹo: Nếu team bạn chưa maintain OpenAPI spec đầy đủ, việc đầu tiên đáng làm không phải là "prompt cho khéo hơn" — mà là generate spec từ code hiện tại (nhiều framework hỗ trợ auto-generate OpenAPI từ annotation). Một spec chính xác dù sinh tự động vẫn là context tốt hơn nhiều so với việc mô tả API bằng lời cho AI đoán.

Làm sao dùng diff của feature branch làm context để khoanh vùng rủi ro và tập trung testing?

Nếu spec context nói cho AI biết "đích đến", thì diff (bản khác biệt mã nguồn) chính là "hướng di chuyển thực tế" của code. Đây là loại context QA thường bỏ qua nhất — nhiều người chỉ đưa ticket/spec mà quên hẳn diff, dẫn đến test case tràn lan, không tập trung vào rủi ro thật.

Diff nói gì với AI

Một diff tốt cho AI biết:

  • File nào bị chạm tới — từ đó suy ra module/layer nào bị ảnh hưởng (UI, service layer, DB migration, config...).
  • Hàm/method nào đổi signature — gợi ý những nơi gọi hàm đó (caller) có khả năng bị ảnh hưởng dây chuyền.
  • Dòng logic nào thêm/xoá — đặc biệt các điều kiện rẽ nhánh (if/else), validation, exception handling.
  • Config/feature flag nào thay đổi — thường là rủi ro cao vì ảnh hưởng runtime behavior mà không đổi code logic.

Nói cách khác, diff giúp AI trả lời câu "risk surface (bề mặt rủi ro) của thay đổi này nằm ở đâu" — thứ mà chỉ đọc ticket không bao giờ cho bạn biết chính xác.

Lấy đúng format diff cần dùng

Sai lầm phổ biến: paste toàn bộ diff khổng lồ (hàng nghìn dòng) vào context, khiến model bị flood và context window (giới hạn dung lượng ngữ cảnh) bị chiếm hết bởi nội dung ít liên quan. Nguyên tắc tôi áp dụng:

  • Với diff lớn: bắt đầu bằng git diff --stat để có overview (file nào, bao nhiêu dòng đổi), sau đó chỉ pull full diff của file thật sự đáng quan tâm.
  • Với PR nhỏ (dưới ~300 dòng đổi): có thể đưa full diff luôn, không cần rút gọn.
  • Luôn kèm commit message/PR description — nó thường giải thích "why" mà diff thuần code không nói được.

Ví dụ lệnh git để tạo bản tóm tắt thay đổi gọn cho AI

git diff --stat main...feature/checkout-refactor

git diff main...feature/checkout-refactor -- '*.ts' '*.go' \
  | grep -E '^[+-].*function |^[+-].*func '

git log main..feature/checkout-refactor --oneline

{
  echo "## Change stat"
  git diff --stat main...feature/checkout-refactor
  echo ""
  echo "## Commit messages"
  git log main..feature/checkout-refactor --pretty=format:"- %s"
} > /tmp/change-summary.txt

File /tmp/change-summary.txt này thường đủ gọn để paste nguyên vào prompt mà vẫn giữ đủ tín hiệu quan trọng cho AI phân tích rủi ro.

Chấm điểm rủi ro (risk-scoring) cho thay đổi từ diff

Không phải file nào đổi cũng rủi ro như nhau. Heuristic tôi hay dùng, và cũng là thứ bạn có thể đưa thẳng vào prompt cho AI tự áp dụng:

  • File thuộc domain payment, auth, permission, data migration → mặc định risk cao.
  • File test tự động bị sửa cùng lúc với code production → có thể chỉ là refactor, nhưng cũng có thể là dev "sửa test cho pass" — cần soi kỹ.
  • File shared/util/common bị đổi → blast radius (phạm vi ảnh hưởng) rộng, ảnh hưởng nhiều feature không liên quan trực tiếp tới ticket.
  • Diff có xoá đoạn validation/exception handling → risk cao dù số dòng đổi ít.
  • Diff chỉ đổi text, style, comment → risk thấp, có thể giảm effort test.

Prompt ví dụ:

Dưới đây là git diff --stat và danh sách commit của nhánh feature/checkout-refactor:
<paste change-summary.txt>

Hãy chấm risk score (High/Medium/Low) cho từng file thay đổi, dựa trên:
- Domain nhạy cảm (payment, auth, data integrity) → ưu tiên High
- Blast radius nếu là file shared/util
- Có đụng vào validation/error handling không
Giải thích ngắn gọn lý do cho mỗi mức risk.

Dùng Diff Context để khoanh vùng regression có chủ đích

Một trong những giá trị lớn nhất của diff context là giúp bạn tránh chạy full regression suite (tốn thời gian) khi không cần thiết. Kết hợp diff với bản đồ phụ thuộc (dependency map) hoặc CODEOWNERS, bạn có thể yêu cầu AI: "những module nào phụ thuộc vào file X vừa đổi, và test suite nào đang cover các module đó?" — từ đó chỉ chạy đúng phần regression liên quan, thay vì regression toàn bộ.

Phân tích diff theo từng loại thay đổi

Cách bạn đọc diff nên khác nhau theo loại thay đổi:

Loại thay đổi Đặc điểm diff Trọng tâm test
Bug fix Diff nhỏ, thường 1-2 file Test đúng defect đã báo + case liền kề dễ bị side-effect
Refactor (không đổi behavior) Diff rộng nhiều file, logic tương đương Regression đảm bảo behavior không đổi, không cần test case mới
Feature mới File mới + điểm tích hợp bị sửa Test case mới theo spec + test tích hợp với phần cũ
Nâng version dependency Diff ở file lock/manifest Đọc changelog dependency, smoke test các luồng dùng lib đó

Mẹo: Khi review PR refactor "không đổi behavior", hãy yêu cầu AI so sánh diff với behavior cũ và tự khẳng định "output trước và sau thay đổi phải giống nhau trong mọi case đã biết" — nếu AI không tự tin khẳng định được điều này, đó là dấu hiệu refactor thực ra có side-effect và cần review kỹ hơn, không phải chỉ chạy lại test cũ cho có.

Spec Context và Code-Change Context kết hợp với nhau để phát hiện gap như thế nào?

Đây là phần "ăn tiền" của mô hình Dual-Context: khi bạn đưa cả hai loại context vào cùng một prompt, AI không chỉ generate test case — nó có thể phát hiện ra gap (khoảng trống) giữa cái nên làm và cái đã làm, việc mà nếu chỉ đọc code hoặc chỉ đọc spec riêng lẻ, con người (và AI) rất dễ bỏ sót.

Các loại gap mà Dual-Context phát hiện được

  • Missing implementation: spec yêu cầu hành vi X, nhưng diff không hề implement X — feature thiếu sót, có thể do dev quên hoặc hiểu sai ticket.
  • Undocumented behavior / scope creep: diff làm ra hành vi Y, nhưng spec chưa bao giờ nhắc đến Y — có thể là "thêm mắm thêm muối" ngoài scope, cần confirm với PM có được duyệt không.
  • Edge case gap: spec có nhắc đến case đặc biệt (ví dụ "nếu hết hàng thì báo lỗi"), nhưng đoạn code xử lý lỗi trong diff không cover đúng case đó.
  • Test coverage gap: acceptance criteria có item rõ ràng, nhưng không có test file/test case nào trong diff tương ứng — cảnh báo automation chưa theo kịp yêu cầu.

Prompt phân tích Dual-Context đầy đủ

Đây là prompt tôi dùng thường xuyên nhất khi review PR trước khi ký duyệt QA:

Bạn là senior QA engineer thực hiện gap analysis giữa requirement và implementation.

CONTEXT 1 — SPEC (hành vi kỳ vọng):
<paste acceptance criteria / user story>

CONTEXT 2 — CODE CHANGE (thay đổi thực tế):
<paste git diff hoặc diff --stat kèm các hunk liên quan>

NHIỆM VỤ:
1. Liệt kê mọi hành vi được mô tả trong spec nhưng KHÔNG thấy được implement trong diff.
2. Liệt kê mọi hành vi được implement trong diff nhưng KHÔNG được mô tả trong spec (hành vi ngoài scope / chưa document).
3. Liệt kê các edge case được nhắc trong spec mà đoạn xử lý lỗi trong diff chưa cover.
4. Đánh dấu file test nào trong diff không map với item nào trong spec, và item spec nào chưa có test file tương ứng.
5. Với mỗi gap, chấm severity (High/Medium/Low) và gợi ý 1 test case cụ thể để lấp gap đó.

Output dạng bảng markdown: Gap Type | Description | Severity | Suggested Test Case.

Prompt này chạy tốt nhất khi bạn đưa spec context đã được cấu trúc hoá (Gherkin, bảng) và diff context đã được rút gọn đúng scope — nếu cả hai còn thô, output sẽ chung chung, không actionable.

Mẹo: Chạy prompt gap analysis này TRƯỚC khi bạn tự viết test case, không phải sau. Gap analysis giúp bạn biết ngay từ đầu nên tập trung effort vào đâu, tránh viết một lô test case đầy đủ mà bỏ sót chính cái gap quan trọng nhất.

Nên làm gì khi chỉ có một loại context?

Thực tế không phải lúc nào bạn cũng có đủ cả hai. Startup tốc độ nhanh thường thiếu spec chính thức; sprint đầu của feature mới thì chưa có code. Biết cách xử lý từng tình huống thiếu hụt là kỹ năng thực dụng bạn cần có.

Khi chỉ có Spec Context (chưa có code)

Tình huống: sprint mới bắt đầu, ticket đã có acceptance criteria rõ ràng, dev chưa code xong hoặc chưa bắt đầu. Đây là thời điểm tốt để làm test design shift-left — dùng AI generate test case skeleton, test plan, review độ rõ ràng của acceptance criteria (đặt câu hỏi ngược cho PM nếu spec mập mờ) ngay từ giờ, không cần chờ code.

Lưu ý: output ở giai đoạn này chỉ nên coi là "khung sườn" (skeleton) — chưa thể verify với implementation thật, nên chưa chốt test case cuối cùng, và cần review lại khi diff xuất hiện.

Khi chỉ có Code-Change Context (không có spec)

Tình huống: code legacy không tài liệu, hoặc team làm việc nhanh không kịp viết spec chính thức. AI buộc phải suy luận ngược "hành vi này nên như vậy" từ chính code, comment, và test có sẵn.

Rủi ro lớn nhất ở đây: AI (và cả con người) dễ nhầm "code đang làm gì" thành "code nên làm gì" — nếu code đang có bug, AI có thể "hợp pháp hoá" bug đó thành behavior đúng trong bộ test case sinh ra.

Cách giảm rủi ro:

  • Dùng commit message, PR description, ticket link như "spec thay thế" — luôn tốt hơn không có gì.
  • Yêu cầu AI viết ra "spec suy luận được" (inferred spec) từ code TRƯỚC, để con người review và xác nhận trước khi dùng nó generate test case — đừng để AI nhảy thẳng từ code sang test case mà bỏ qua bước xác nhận trung gian này.

Khi cả hai loại context đều không đầy đủ

Tình huống khó nhất: không có spec, diff cũng mơ hồ (ví dụ squashed commit không message). Lúc này, đừng để AI generate test case trực tiếp — hãy dùng AI để soạn câu hỏi làm rõ gửi cho PM/dev, hoặc để lên kế hoạch exploratory testing (kiểm thử thăm dò) thủ công trước để tự bạn xác lập "sự thật" (ground truth) về hệ thống, rồi mới quay lại dùng AI một cách có cơ sở.

Về lâu dài, đây cũng là dữ liệu đáng đưa vào retro: nếu team liên tục rơi vào tình huống thiếu cả hai loại context, đó là process gap cần được PM/Lead giải quyết, không chỉ là vấn đề kỹ thuật prompt.

Mẹo: Khi thiếu context, đừng cố "vá" bằng cách viết prompt dài hơn hay ra lệnh chi tiết hơn — vấn đề không nằm ở prompt, nằm ở dữ liệu đầu vào. Dành 10 phút hỏi lại người liên quan để có đúng loại context còn thiếu, luôn hiệu quả hơn cố gắng "prompt engineering" để AI tự bù đắp phần thông tin nó không có.