Menu

Prompt review code: cách review code bằng AI

Một prompt review code tốt nói rõ cần tìm gì, yêu cầu mức độ nghiêm trọng cho mỗi phát hiện, đòi một đầu vào cụ thể gây lỗi kèm một diff, và dặn model bỏ qua chuyện phong cách. Sau đó bạn kiểm tra từng phát hiện, vì một số sẽ sai.

Mọi prompt bên dưới đều có thể chỉnh sửa: thay đổi nó, rồi mở trong ChatGPT, Claude hoặc ứng dụng AI khác.

Một prompt review code nên nói cho model biết cần tìm gì, mỗi phát hiện nghiêm trọng đến mức nào, và cần đưa ra bằng chứng gì. "Review đoạn code này" sẽ nhận về một danh sách góp ý thân thiện, trong đó một lỗ hổng bảo mật nằm lẫn giữa mẹo viết docstring và một sở thích đặt tên. Một prompt review có checklist, mức độ nghiêm trọng và yêu cầu diff sẽ nhận về một báo cáo bạn có thể xử lý ngay, từng phát hiện một.

Từ "review đoạn này" đến checklist có mức độ nghiêm trọng

Đoạn code ở cả hai tab đều có hai bug thật. Hãy so sánh cách mỗi câu trả lời trình bày chúng.

Bạn review giúp tôi đoạn code này được không? def get_user(conn, username): cur = conn.cursor() cur.execute(f"SELECT id, email FROM users WHERE name = '{username}'") row = cur.fetchone() return {"id": row[0], "email": row[1]}
Try it
Example replyReplies vary between models and runs.

Nhìn chung hàm ngắn gọn và dễ theo dõi. Một vài góp ý:

  • Thêm type hint, ví dụ username: str, để chữ ký hàm rõ ràng hơn.
  • Thêm docstring giải thích hàm trả về gì.
  • Dùng truy vấn tham số hóa. Ghép SQL bằng f-string có thể dẫn đến SQL injection.
  • Xử lý trường hợp không có người dùng. fetchone() có thể trả về None.
  • Đặt tên: fetch_user có thể mô tả việc truy cập cơ sở dữ liệu rõ hơn.

Câu trả lời đầu tiên tìm ra cả hai bug, và đó chính là vấn đề khi đánh giá một lần review qua những gì nó nhắc tới. Lỗi injection nằm thứ ba trong danh sách năm mục, được nói nhẹ đi thành "có thể dẫn đến", không có cách sửa, đứng cạnh một mẹo về docstring. Người đọc lướt qua sẽ thêm type hint rồi bỏ qua.

Prompt thứ hai thay đổi bốn điều:

  • Phạm vi. "Chỉ bug, bảo mật và xử lý lỗi. Bỏ qua phong cách" loại bỏ phần nhiễu. Phong cách là việc của linter và formatter, chúng nhanh hơn và không bao giờ tự mâu thuẫn.
  • Mức độ nghiêm trọng. Nó buộc model phải xếp hạng, và một danh sách có xếp hạng cho bạn biết nên bắt đầu từ đâu.
  • Một đầu vào gây lỗi. "x' OR '1'='1" biến một rủi ro mơ hồ thành một minh chứng bạn có thể chạy. Đây cũng là bộ lọc tốt nhất cho cảnh báo sai, như phần cuối sẽ cho thấy.
  • Diff. Một diff nhỏ cho mỗi phát hiện có thể được áp dụng hoặc từ chối riêng rẽ. Một hàm được viết lại trộn mọi chỗ sửa với những thay đổi không ai yêu cầu.

"Nếu ở một mức độ nào đó không có gì, đừng bịa ra" quan trọng hơn vẻ ngoài của nó. Khi được nhờ review, model có xu hướng đưa ra phát hiện ngay cả khi chẳng có gì nhiều để tìm, và những phát hiện yếu nhất chỉ làm danh sách dài thêm. Cho phép rõ ràng việc không báo cáo gì ở một mức độ sẽ cắt bớt phần độn đó.

Ngữ cảnh người review cần có

Một người review biết code dùng để làm gì và đầu vào của nó đến từ đâu. Model chỉ biết những gì bạn dán vào. "username lấy thẳng từ một form đăng nhập" là câu biến SQL injection thành một phát hiện mức cao thay vì chỉ là lý thuyết. Ngữ cảnh hữu ích cho một lần review:

  • Đầu vào đến từ đâu: người dùng, một dịch vụ nội bộ khác, một tệp cấu hình do bạn kiểm soát.
  • Cái gì gọi tới đoạn code và nó mong nhận lại gì.
  • Môi trường chạy: phiên bản ngôn ngữ, framework, driver cơ sở dữ liệu. Cách sửa lỗi injection ở trên dùng ? vì sqlite3 dùng placeholder đó; một số driver khác dùng %s.
  • Điều bạn quan tâm nhất với thay đổi này: tính đúng đắn, bảo mật, hiệu năng, hoặc cả ba.

Một prompt review dùng lại được

Khối này biến checklist thành một mẫu. Đổi ngôn ngữ, trọng tâm và code, còn các quy tắc thì giữ nguyên. Tắt phần ràng buộc là cách nhanh để thấy bao nhiêu phần nhiễu quay lại khi thiếu nó.

Mẫu prompt review code
Fill in
Parts
Bạn là một người review JavaScript cấp cao. Bạn chỉ báo cáo vấn đề thật và không gì khác.
Review đoạn code dưới đây, tập trung vào bug và xử lý lỗi.
Đoạn code này chạy trên trình duyệt. `ids` là một mảng id dạng số lấy từ API của chính chúng tôi.
async function loadAll(ids) { const results = []; ids.forEach(async (id) => { const res = await fetch(`/api/items/${id}`); results.push(await res.json()); }); return results; }
Với mỗi phát hiện: mức độ nghiêm trọng (cao, trung bình hoặc thấp), dòng code, một đầu vào hoặc tình huống gây ra nó, và một diff tối thiểu. Nghiêm trọng nhất trước.
Không nhận xét về phong cách, cách đặt tên hay định dạng. Nếu không chắc một phát hiện là thật, hãy nói vậy và nói điều gì sẽ xác nhận nó. Nếu không tìm thấy gì, hãy nói "Không có phát hiện nào".
Try it
Example replyReplies vary between models and runs.

1. Cao: hàm trả về trước khi bất kỳ request nào xong (dòng 3) forEach không chờ các callback async. loadAll trả về mảng results rỗng ngay lập tức, còn các lệnh push xảy ra sau đó. Mọi nơi gọi đều nhận được [].

2. Trung bình: request thất bại bị coi là dữ liệu (dòng 5) Response 404 hay 500 không phải là ngoại lệ trong fetch. Một body lỗi dạng JSON sẽ được phân tích và push vào như thể là một item, còn một body không phải JSON khiến res.json() ném lỗi bên trong một callback không ai await.

Cách sửa cho cả hai, đồng thời giữ kết quả theo đúng thứ tự của ids:

 async function loadAll(ids) {
-  const results = [];
-  ids.forEach(async (id) => {
-    const res = await fetch(`/api/items/${id}`);
-    results.push(await res.json());
-  });
-  return results;
+  return Promise.all(
+    ids.map(async (id) => {
+      const res = await fetch(`/api/items/${id}`);
+      if (!res.ok) throw new Error(`Item ${id}: HTTP ${res.status}`);
+      return res.json();
+    })
+  );
 }

Dòng vai trò đặt ra một tiêu chuẩn ("chỉ vấn đề thật và không gì khác"), đó là chỗ role prompting giúp ích nhiều nhất. Nếu một chương trình đọc kết quả review, chẳng hạn một script đăng các phát hiện thành bình luận trên pull request, hãy yêu cầu JSON với các trường cố định thay vì văn xuôi; đầu ra có cấu trúc chỉ cách làm.

Xử lý cảnh báo sai

Một số phát hiện sẽ sai. Model không thấy được nơi gọi đã kiểm tra đầu vào, ràng buộc cơ sở dữ liệu khiến một trường hợp không thể xảy ra, hay lý do một dòng code trông lạ là có chủ đích. Nó cũng có thể hiểu sai cách một thư viện hoạt động; ảo giác AI giải thích vì sao một khẳng định tự tin chưa phải là một khẳng định đã được kiểm chứng.

Hãy thử từng phát hiện với đầu vào gây lỗi của nó. Nếu bạn không thể tạo ra một đầu vào chạm tới vấn đề trong hệ thống của mình, hãy phản biện bằng phần ngữ cảnh còn thiếu thay vì chấp nhận cách sửa:

Phản biện một phát hiện
Bạn đã đánh dấu hàm này vì `cents` có thể là undefined hoặc một chuỗi: function formatPrice(cents) { return `$${(cents / 100).toFixed(2)}`; } Nó chỉ được gọi từ `renderCart`, hàm này return sớm trừ khi `Number.isInteger(cents)` là true. Đây có còn là vấn đề không? Nếu không, hãy rút lại phát hiện đó.
Try it
Example replyReplies vary between models and runs.

Với phép kiểm tra đó thì không: formatPrice chỉ luôn nhận một số nguyên, nên phát hiện này không áp dụng và tôi rút lại.

Có một điều phép kiểm tra chưa bao quát: số nguyên âm vẫn qua được Number.isInteger, và formatPrice(-500) trả về "$-5.00". Nếu tiền hoàn lại hoặc giảm giá có thể đi tới hàm này, có lẽ bạn muốn "-$5.00". Nếu không, chẳng có gì cần thay đổi.

Một model khi được đưa bằng chứng mới nên hoặc rút lại phát hiện, hoặc giải thích vì sao nó vẫn đúng khi đã xét đến bằng chứng đó. Cả hai đều hữu ích. Một model cứ đồng ý với mọi phản biện thì không phải đang review, nên hãy đánh giá câu trả lời qua lý lẽ của nó, chứ không phải qua việc nó có đồng ý hay không. Với code do chính model viết, hãy review trong một cuộc trò chuyện mới, để việc review không bị nhìn qua cùng lối suy luận đã tạo ra đoạn code. Cũng những thói quen về phạm vi, ngữ cảnh và test giúp code tốt hơn ngay từ đầu; xem prompt để viết code.

Câu hỏi thường gặp

Prompt tốt để review code là gì?

Nêu rõ cần review những gì (bug, bảo mật, xử lý lỗi), yêu cầu mức độ nghiêm trọng cho mỗi phát hiện, và đòi một đầu vào cụ thể gây ra vấn đề cùng một diff để sửa nó. Dặn model bỏ qua định dạng và cách đặt tên trừ khi được hỏi. Cung cấp cho nó ngữ cảnh mà một người review sẽ có: code dùng để làm gì và cái gì gọi tới nó.

AI có thể thay thế việc review code của con người không?

Không, nhưng nó là một lượt review đầu hữu ích. Nó nhanh, và giỏi phát hiện các kiểu bug phổ biến như None chưa được xử lý, thiếu await hay SQL ghép từ chuỗi. Nó không biết các quy tắc của sản phẩm, phần còn lại của codebase hay lý do một quyết định được đưa ra, và một số phát hiện của nó sẽ sai. Hãy dùng nó trước khi người thật review, chứ không phải thay cho người thật.

Vì sao AI khi review code lại báo những vấn đề không có thật?

Model chỉ thấy đoạn code bạn dán vào. Nếu một giá trị đã được nơi gọi kiểm tra, hoặc một trường hợp không bao giờ xảy ra trong hệ thống của bạn, model không thể biết điều đó nên vẫn đánh dấu rủi ro. Yêu cầu một đầu vào cụ thể gây lỗi sẽ lọc bớt nhiều trường hợp như vậy: một phát hiện không có đầu vào thực tế nào kích hoạt được thường là cảnh báo sai.

Có nên nhờ AI viết lại code trong lúc review không?

Hãy yêu cầu các diff nhỏ, mỗi phát hiện một diff, thay vì một tệp được viết lại. Viết lại toàn bộ sẽ trộn các chỗ sửa với những thay đổi không ai yêu cầu, và bạn lại phải review cả bản viết lại. Diff cho phép bạn chấp nhận hoặc từ chối từng phát hiện một cách riêng rẽ.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu