Có lần tôi nhờ AI sửa một bug nhỏ trong màn hình filter. Bug thật: khi đổi date range, query param không reset page về 1. Diff AI trả về nhìn gọn, test pass, UI chạy được. Nhưng nó âm thầm đổi luôn default sort của list vì “thấy hợp lý hơn”. Nếu chỉ nhìn đoạn fix chính, tôi đã merge. May là tôi có thói quen đọc file list trước khi đọc code.
Review code AI viết không khác review code người viết ở mục tiêu: bảo vệ behavior, contract, maintainability. Khác ở pattern lỗi. Người thường hay thiếu case, quên edge, đặt tên tệ. AI hay làm một kiểu khác: sửa quá scope, tạo abstraction không cần, mock dữ liệu để test qua, hoặc làm code “trông đúng” nhưng lệch domain rule.
Vì vậy tôi không review từ trên xuống theo cảm xúc. Tôi review theo checklist.
Nhìn file list trước khi đọc logic
Command đầu tiên:
git status --short
git diff --stat
Tôi muốn biết AI đã chạm vào đâu. Nếu task là sửa OrderFilters.tsx mà diff có apiClient.ts, routes.ts, package-lock.json, hoặc file translation unrelated, đó là tín hiệu đỏ. Không phải mọi file ngoài scope đều sai, nhưng phải có lý do rõ.
File list trả lời ba câu hỏi:
- Blast radius có đúng với task không?
- Có shared file nào bị sửa ngoài dự kiến không?
- Có generated, lockfile, env, build artifact nào lọt vào không?
Đừng bắt đầu bằng cách đọc component. Bắt đầu bằng cách xem AI có tôn trọng ranh giới không. Một diff đẹp trong file chính vẫn có thể kèm một thay đổi nguy hiểm ở file phụ.
Đọc request gốc cạnh diff
AI review dễ bị lừa bởi code sạch. Nhưng code sạch mà làm sai task vẫn là bug. Tôi thường mở prompt/task cạnh diff và đánh dấu từng yêu cầu:
- Behavior cần thêm là gì?
- Behavior nào phải giữ nguyên?
- Boundary nào user đã nói không được đụng?
- Verification nào được yêu cầu?
Ví dụ user nói “reuse existing endpoint”. Nếu diff thêm endpoint mới, fail ngay cả khi endpoint chạy. User nói “không dùng mock data”. Nếu test tự tạo mock response để tránh gọi hook thật trong khi project đã có test helper, cần hỏi lại hoặc sửa.
Review AI không phải tìm “code có chạy không” trước. Phải tìm “code có làm đúng việc được giao không” trước.
Tìm behavior drift
Behavior drift là lỗi tôi gặp nhiều nhất: AI sửa một bug nhưng kéo theo đổi hành vi khác. Một vài dấu hiệu:
- Đổi default value.
- Đổi sort/filter/pagination semantics.
- Đổi error fallback text.
- Đổi empty state.
- Đổi cache key hoặc dependency array.
- Đổi response mapping để “đơn giản hơn”.
Trong frontend, tôi xem kỹ useMemo, useEffect, event handler, query key, form reset. Trong backend, tôi xem validation, authorization check, transaction boundary, idempotency, retry behavior.
Nếu AI thêm một helper mới, tôi hỏi: helper đó encode rule domain hay chỉ bọc một dòng code? Nếu chỉ bọc một dòng code, thường không cần. Nếu helper đổi semantic, phải có test.
Không tin test chỉ vì test pass
Test pass là điều kiện cần, không phải bằng chứng đủ. AI rất giỏi viết test khớp implementation mới. Nó cũng rất giỏi mock sai lớp để tránh chạm bug thật.
Tôi đọc test theo các câu hỏi:
- Test có fail trên code cũ không?
- Test assert behavior user thấy, hay assert implementation detail?
- Mock có che mất contract thật không?
- Test data có giống domain thật không?
- Có case regression trực tiếp từ bug không?
Ví dụ bug là query param không reset page khi đổi filter. Test tốt sẽ render/hook action, đổi filter, assert URL hoặc query state có page=1. Test yếu sẽ gọi function nội bộ buildParams với object tự chế rồi assert output. Test đó có ích, nhưng chưa đủ nếu bug nằm ở event flow.
AI hay thêm snapshot hoặc unit test nông vì nhanh. Với code AI viết, tôi thích ít test hơn nhưng đúng seam hơn: contract test cho API shape, component test cho interaction, integration test cho flow quan trọng.
Bắt mock data và endpoint mới
Trong project thật, mock data là nguồn bug âm thầm. AI thấy thiếu API thường tự tạo array mẫu, fake hook, hoặc thêm endpoint local. Nếu guideline nói “API endpoints already exist”, review phải search trước:
rg -n "useOrders|orders" src
rg -n "/orders|orders/" src
Nếu existing hook có sẵn mà AI tạo hook mới, diff phải bị trả lại. Nếu endpoint có sẵn nhưng response shape hơi khó, AI phải map đúng shape, không được đổi API contract cho tiện.
Trong backend cũng vậy. Đừng để AI tạo route mới vì không tìm thấy route cũ. Có thể route cũ nằm trong module khác, generated client, hoặc feature-specific helper. Review tốt là đọc repo thật, không tin lời AI nói “không thấy”.
Tìm dấu hiệu over-engineering
AI có khuynh hướng biến một bug nhỏ thành framework nhỏ:
- Thêm type generic quá rộng.
- Tạo config registry cho một case.
- Tách component thành nhiều file dù không reuse.
- Thêm fallback cho impossible state.
- Thêm dependency mới để xử lý việc standard library làm được.
Không phải abstraction nào cũng sai. Nhưng abstraction phải trả tiền thuê nhà: giảm duplication thật, giữ domain rule ở một chỗ, hoặc bám pattern có sẵn trong repo. Nếu không, nó chỉ làm future diff khó đọc hơn.
Tôi hay hỏi: “Nếu revert abstraction này và inline code, behavior có đổi không?” Nếu không đổi, khả năng cao nên bỏ. AI coding hiệu quả nhất khi diff nhỏ và boring. Boring không có nghĩa là kém. Boring là dễ review, dễ rollback, dễ debug lúc 2 giờ sáng.
Kiểm operational detail
Sau logic, tôi scan những thứ rất đời thường:
console.log,debugger, temporary comment.- Unused import.
- Import order lệch convention.
.env.*bị stage.- Lockfile đổi vì command install nhầm.
- Markdown thiếu blank line hoặc code fence language.
- Error bị swallow không
console.errortrong codebase yêu cầu log.
Những lỗi này nhỏ nhưng nói lên một điều: agent chưa hoàn tất cleanup. Với AI diff, cleanup là phần mình không được bỏ qua vì AI có thể dừng ngay sau khi test pass.
Review rollback path
Tôi luôn tự hỏi: nếu merge xong có incident, revert có sạch không? Một diff tốt có rollback path đơn giản:
- Một commit nhỏ.
- Không trộn refactor với bug fix.
- Không đổi schema và UI trong cùng diff nếu không cần.
- Không gom unrelated formatting.
Nếu rollback phải gỡ tay trong sáu file unrelated, diff đó chưa sẵn sàng. Đây là lý do tôi khó tính với scope. AI có thể sửa nhanh, nhưng production không quan tâm sửa nhanh. Production quan tâm khi sai thì quay lại nhanh được không.
Một checklist ngắn tôi dùng
Trước khi approve code AI viết, tôi muốn tự trả lời được:
File list có đúng scope không?
Behavior gốc có được giữ không?
Contract API/schema có đổi không?
Test có bắt regression thật không?
Có mock data, debug log, endpoint mới, dependency mới không?
Rollback có đơn giản không?
Nếu một câu trả lời là “không biết”, tôi chưa approve. Tôi đọc tiếp hoặc yêu cầu agent chứng minh bằng test/diff nhỏ hơn.
AI làm tốc độ viết code rẻ đi, nhưng tốc độ review không nên rẻ theo. Reviewer vẫn phải giữ tiêu chuẩn như với người thật, thậm chí cứng hơn ở phần scope và contract. Code AI viết không cần bị nghi ngờ vì nó là AI. Nó cần bị review vì nó là code sẽ chạy trong hệ thống thật.