06. QA, Code Review & PR Workflow — Vibe Coding Series

Quy trình QA và code review khi phần lớn code do AI viết — automated layer, AI-assisted review, và PR workflow giữ history sạch.

Code pass lint. Test xanh hết. CI chạy ngon. Tôi tự tin merge vào main.

Hôm sau, một senior trong team review lại và gọi tôi: “Này, cái auth middleware này có 3 vấn đề security. Một là nó không validate iss claim trong JWT. Hai là error message trả về quá chi tiết — attacker có thể biết email nào đã đăng ký. Ba là không có rate limiting trên login endpoint.”

Tất cả 3 vấn đề đó, AI đã không viết test cho. Lint không bắt được. CI không phát hiện. Nhưng một người có kinh nghiệm nhìn qua 5 phút là thấy ngay.

Đây là bài học quan trọng nhất của Phase 5: automated QA và AI review là lớp lọc đầu tiên, không phải lớp cuối cùng.

QA Pipeline — từ AI-generated code qua automated checks, AI review, đến human final check và main


Bối cảnh

Ở bài 05, tôi đã implement 8 task auth của TaskFlow với single agent và multi-agent, commit từng task nhỏ, và có HITL checkpoint trước khi merge. Bài này bước vào Phase 5: trước khi code lên main, nó phải đi qua một quy trình QA và review có cấu trúc.


Automated QA layer

Automated QA không thay thế human review — nó loại bỏ những vấn đề cơ bản để human reviewer có thể tập trung vào những thứ quan trọng hơn.

Static analysis: ESLint + TypeScript strict

Với AI-generated code, TypeScript strict mode là bắt buộc. AI thường viết code “đủ để chạy” nhưng không đủ type-safe — và lỗi type thường ẩn những bug logic thực sự.

Config tôi dùng cho TaskFlow:

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  }
}
// eslint.config.js
export default [
  {
    rules: {
      "@typescript-eslint/no-explicit-any": "error",
      "@typescript-eslint/no-floating-promises": "error",
      "@typescript-eslint/no-unused-vars": "error",
      "no-console": "warn",
      "eqeqeq": ["error", "always"]
    }
  }
]

noUncheckedIndexedAccess đặc biệt hữu ích với AI code — nó bắt buộc bạn handle trường hợp array index có thể undefined, thứ mà AI thường bỏ qua.

Security scan: AI-assisted SAST

SAST (Static Application Security Testing) truyền thống như Semgrep hay Snyk chạy tốt trong CI. Nhưng với AI-generated code, tôi thêm một bước nữa: dùng AI để review code từ góc độ security trước khi push.

Prompt tôi dùng sau khi AI viết xong một feature:

Review đoạn code này từ góc độ security. Tập trung vào:
1. Input validation — có validate tất cả user input không?
2. Authentication/Authorization — có check đúng permission không?
3. Information disclosure — error message có tiết lộ thông tin nhạy cảm không?
4. Injection — có SQL injection, command injection, hoặc path traversal không?
5. Cryptography — có dùng thuật toán yếu hoặc hardcode secret không?

Với mỗi vấn đề tìm được, chỉ rõ dòng code và giải thích tại sao đó là vấn đề.

[paste code]

Đây không phải thay thế cho Semgrep — đây là bước bổ sung để bắt những vấn đề mà rule-based scanner bỏ sót.

Test coverage threshold

Với AI-generated code, coverage threshold là safety net quan trọng. AI thường viết test cho happy path nhưng bỏ qua edge case.

// vitest.config.ts
{
  "coverage": {
    "thresholds": {
      "lines": 80,
      "functions": 80,
      "branches": 70
    },
    "exclude": ["**/*.test.ts", "src/types/**"]
  }
}

Branch coverage 70% là ngưỡng tôi thấy thực tế — đủ để bắt những path quan trọng mà không quá khắt khe đến mức AI phải viết test cho mọi edge case nhỏ.


AI-assisted code review

Dùng AI để review AI code

Nghe có vẻ circular, nhưng thực ra rất hiệu quả — với điều kiện bạn dùng đúng prompt. AI review AI không phải để “AI tự kiểm tra mình”, mà là dùng AI như một reviewer có kiến thức rộng để bắt những pattern phổ biến.

Prompt pattern hiệu quả nhất tôi đã dùng:

Bạn là senior engineer đang review PR này. Code được viết bởi AI agent.

Context:
- Project: TaskFlow — task management SaaS
- Feature: User authentication (JWT + refresh token)
- Tech stack: Node.js, TypeScript, PostgreSQL

Hãy review code dưới đây và tạo review comment theo format:
[CRITICAL] — vấn đề phải fix trước khi merge
[SUGGESTION] — cải thiện nên làm nhưng không blocking
[QUESTION] — điểm cần clarification

Focus vào:
- Security: authentication, authorization, input validation
- Performance: N+1 query, missing index, unnecessary computation
- Maintainability: naming, complexity, coupling

[paste diff hoặc code]

Kết quả thường ra 5–10 comment có chất lượng. Không phải tất cả đều đúng — AI đôi khi flag false positive — nhưng nó tiết kiệm thời gian đáng kể so với đọc từng dòng.

Tạo review checklist từ spec

Một trick tôi thấy rất hữu ích: dùng AI để tạo review checklist từ acceptance criteria của task.

Từ acceptance criteria này, tạo review checklist để verify implementation:

Acceptance criteria:
- POST /auth/login nhận { email, password }
- Return 200 + { accessToken, refreshToken } hoặc 401
- Lưu refresh token vào store
- Rate limit: tối đa 5 lần thử trong 15 phút

Checklist format:
- [ ] [tên check]: [cách verify]

AI sẽ tạo ra checklist như:

  • [ ] Login với email/password đúng → verify response có accessToken và refreshToken
  • [ ] Login với password sai → verify response 401, không có token
  • [ ] Login với email không tồn tại → verify response 401, error message không tiết lộ email có tồn tại không
  • [ ] Thử login 6 lần liên tiếp → verify lần thứ 6 bị 429 Too Many Requests
  • [ ] Refresh token được lưu vào database → verify bằng query trực tiếp

Checklist này vừa là test case, vừa là review guide cho human reviewer.


PR workflow

PR template với AI-generated summary

Với AI-generated code, PR description thường bị bỏ trống hoặc viết qua loa. Tôi dùng template bắt buộc và AI để generate summary:

<!-- .github/pull_request_template.md -->
## Summary
<!-- AI-generated: paste output từ prompt bên dưới -->

## Changes
- [ ] Feature implementation
- [ ] Tests added/updated
- [ ] Documentation updated

## Security checklist
- [ ] Input validation đầy đủ
- [ ] No hardcoded secrets
- [ ] Error messages không tiết lộ thông tin nhạy cảm
- [ ] Rate limiting (nếu applicable)

## Test coverage
- Unit tests: X%
- Integration tests: covered / not covered
- Manual testing: [mô tả những gì đã test thủ công]

## AI involvement
- [ ] Code generated by AI agent
- [ ] AI security review done
- [ ] Human final review done

## Breaking changes
<!-- Có thay đổi nào break backward compatibility không? -->

Để generate phần Summary, tôi dùng prompt:

Tóm tắt PR này trong 3–5 câu cho reviewer. Bao gồm:
- Feature gì được implement
- Approach chính (không cần chi tiết implementation)
- Điểm nào cần reviewer chú ý đặc biệt

[paste git diff --stat và một số file thay đổi chính]

Branch strategy với multi-agent

Khi dùng multi-agent (như bài 05), mỗi agent làm việc trên branch riêng. Trước khi tạo PR, tôi merge các branch agent vào một integration branch:

# Tạo integration branch
git checkout -b feature/auth-integration

# Merge từng agent branch theo thứ tự dependency
git merge feature/auth-utilities    # Task 1, 2
git merge feature/user-schema       # Task 3
git merge feature/auth-endpoints    # Task 5, 6, 7, 8

# Resolve conflict nếu có, chạy test
npm test

# Tạo PR từ integration branch vào main

Lý do không tạo PR trực tiếp từ từng agent branch: reviewer sẽ phải review 4–5 PR riêng lẻ và tự hiểu mối quan hệ giữa chúng. Một integration branch gom tất cả lại, dễ review hơn nhiều.

Squash vs. merge: giữ history sạch

Với AI-generated code, commit history thường rất noisy — AI hay tạo commit kiểu “fix”, “update”, “refactor” không có context. Tôi dùng squash merge khi merge PR vào main:

# Squash merge: gom tất cả commit trong PR thành 1 commit
git merge --squash feature/auth-integration
git commit -m "feat(auth): implement JWT authentication with refresh token

- hashPassword/verifyPassword utilities (bcrypt, salt 12)
- JWT access token (15min) + refresh token (7 days)
- Register, login, refresh, logout endpoints
- Auth middleware for protected routes
- Rate limiting on login endpoint (5 req/15min)"

Commit message cuối cùng này tôi viết tay — đây là nơi human judgment quan trọng. AI có thể draft, nhưng bạn phải review và đảm bảo nó mô tả đúng những gì thực sự thay đổi.


Ví dụ thực chiến: PR cho auth feature của TaskFlow

Sau khi implement xong 8 task auth ở bài 05, đây là quy trình tôi đi qua:

Bước 1: Automated checks

npm run lint          # ESLint + TypeScript check
npm run test          # Vitest với coverage
npm run test:security # Semgrep scan

Kết quả: 2 TypeScript error (AI dùng any ở 2 chỗ), coverage 84%, Semgrep clean.

Fix 2 TypeScript error, chạy lại — tất cả pass.

Bước 2: AI security review

Paste toàn bộ diff vào Claude với prompt security review ở trên. Kết quả:

  • [CRITICAL] Login endpoint không validate email format trước khi query database
  • [CRITICAL] Error message ở register endpoint tiết lộ “Email already exists” — nên trả về generic message
  • [SUGGESTION] Thêm httpOnly flag cho cookie nếu sau này dùng cookie-based auth
  • [QUESTION] Refresh token có được rotate sau mỗi lần dùng không?

Fix 2 critical issue, note lại suggestion và question để discuss với team.

Bước 3: Tạo PR

Dùng template, AI generate summary, điền checklist. PR description rõ ràng, reviewer biết cần focus vào đâu.

Bước 4: Human final review

Reviewer (tôi, sau 1 ngày) đọc lại với fresh eyes. Phát hiện thêm: refresh token store không có index trên userId column — sẽ slow khi deleteAllUserTokens. Thêm migration để tạo index.

Bước 5: Squash merge

Viết commit message tổng hợp, merge vào main.

Tổng thời gian: ~45 phút cho toàn bộ quy trình QA + review. Không có quy trình này, những vấn đề trên sẽ lên production.


Những điều cần tránh

Dùng AI review AI mà không có human final check. AI reviewer và AI author có cùng blind spot — cả hai đều được train trên cùng data, cùng pattern. Những vấn đề mà AI author bỏ qua thường là những vấn đề AI reviewer cũng bỏ qua. Human final check không cần đọc từng dòng, nhưng phải có.

PR quá lớn. Khi dùng multi-agent, cám dỗ là gom tất cả vào một PR lớn. PR với 50+ file thay đổi không ai review kỹ được — reviewer sẽ approve cho xong. Giới hạn PR ở mức 200–300 dòng thay đổi là ngưỡng thực tế để review có chất lượng.


Tóm tắt

  • Automated QA (lint, type check, coverage) loại bỏ vấn đề cơ bản để human reviewer tập trung vào điều quan trọng hơn
  • AI security review bắt được pattern phổ biến — dùng prompt có cấu trúc, không phải “review code này”
  • PR template + AI-generated summary giúp reviewer hiểu context nhanh hơn
  • Squash merge giữ history sạch khi AI tạo nhiều commit noisy
  • Human final check là bắt buộc — AI reviewer và AI author có cùng blind spot

Tiếp theo

Code đã pass QA, đã được review, đã merge vào main. Bước cuối cùng: đưa lên production một cách an toàn — feature flag, canary deployment, và observability để biết ngay khi có vấn đề.

→ Bài 07: Deploy, Rollback & Observability

← Bài 05: Thực thi với AI Agent