"Hoàn thiện trang chủ của cổng thông tin" - một yêu cầu như vậy có thể ẩn chứa nhiều việc khác nhau. Danh sách khuyến mãi thiếu bộ lọc. Nút trên banner dẫn sai chỗ. Phần chuẩn bị dữ liệu cho bảng xếp hạng được viết lặp lại, khiến mỗi lần sửa tốn gấp đôi công sức. Còn địa chỉ khuyến mãi lấy từ đâu thì hầu như chẳng ai nhớ rõ.

Người nhận yêu cầu này có lẽ sẽ hỏi lại. Claude Code cũng có thể hỏi lại, nhưng thường thì nó tự lấp chỗ trống bằng những việc nghe có vẻ hợp lý mà chẳng ai yêu cầu. Với mỗi loại nhiệm vụ, phần tự ý làm thêm lại khác nhau: bộ lọc được kèm thêm ô tìm kiếm, sửa liên kết kèm giao diện mới, refactoring tiện thể "sửa" luôn cách sắp xếp, còn việc tìm hiểu kết thúc bằng những thay đổi trong mã.

Điều gì cần thay đổi

Cách dễ nhất để phân biệt các loại nhiệm vụ là nhìn vào điều sẽ khác đi khi công việc hoàn tất:

  • xuất hiện hành vi trước đây chưa có - feature, một tính năng mới;
  • hành vi được đưa về đúng như mong đợi - bugfix, sửa lỗi. Trường hợp đã được triển khai nhưng ngay từ đầu hoạt động khác với thỏa thuận cũng thuộc loại này;
  • bề ngoài mọi thứ vẫn như cũ, nhưng mã bên trong được tổ chức khác đi - refactoring;
  • bạn nhận được câu trả lời cho một câu hỏi về dự án, còn mã nguồn vẫn nguyên trạng - research, tìm hiểu.

Loại nhiệm vụ gợi ý những gì cần nêu rõ trong yêu cầu và những gì cần kiểm tra. Mức độ tự do có thể giao cho agent còn tùy thuộc vào cái giá của sai sót và quy mô nhiệm vụ: sửa mã đang được người dùng sử dụng cần cẩn trọng hơn tạo một trang mới trong dự án thực hành. Cũng có nhiệm vụ kết hợp, chẳng hạn tìm hiểu rồi mới sửa. Khi đó, mỗi giai đoạn cần có kết quả và ranh giới riêng.

Thêm tính năng

Danh sách khuyến mãi cần một công tắc chỉ hiển thị các khuyến mãi đang hoạt động. Hãy xác định điều kiện ngay từ đầu: một khuyến mãi được xem là đang hoạt động nếu có thuộc tính active: true; khuyến mãi A và C bật thuộc tính này, còn B thì không. Dữ liệu ở đây chỉ để thực hành, còn các yêu cầu bên dưới là mẫu để bạn áp dụng cho dự án của mình.

Agent có lẽ sẽ thực hiện yêu cầu "Thêm bộ lọc các khuyến mãi đang hoạt động" và làm thêm một chút nữa. Kết quả có thể như sau: công tắc đã xuất hiện, bên cạnh có ô tìm kiếm "đằng nào cũng lọc rồi", thêm cả sắp xếp theo ngày kết thúc. Từng bổ sung riêng lẻ đều có vẻ hợp lý. Gộp lại, chúng biến một tính năng thành một hệ thống con nhỏ mà chẳng ai yêu cầu, và giờ bạn cũng phải kiểm tra nó.

Ít nhất, những phần làm thêm còn hiện ra trên trang. Tệ hơn là khi agent âm thầm tự quyết định thay bạn. Andrej Karpathy từng kể về dự án MenuGen của mình: người dùng đăng nhập bằng Google, thanh toán qua Stripe, và agent liên kết giao dịch với tài khoản bằng email. Ai dùng hai địa chỉ email khác nhau ở hai dịch vụ thì khoản thanh toán không đến được tài khoản của người đó. Các quyết định về dữ liệu - liên kết bản ghi theo tiêu chí nào và xử lý ra sao những dữ liệu đã có trong dự án - là do bạn đưa ra, tốt nhất hãy nêu rõ ngay trong yêu cầu. Trường hợp của chúng ta đơn giản hơn: khuyến mãi không có thuộc tính active thì xử lý thế nào?

Một nguồn khác dẫn đến việc agent tự ý làm thêm là mã mẫu. Agent tìm đoạn mã tương tự trong dự án rồi bắt chước, nhưng đoạn tương tự ấy có thể lại chính là phần mà mọi người đã muốn viết lại từ lâu. Nếu dự án có ví dụ tốt, hãy chỉ rõ cho agent.

Với một tính năng mới, hãy nêu một kịch bản người dùng và danh sách cụ thể những việc không thuộc phạm vi:

Thêm công tắc "Chỉ khuyến mãi đang hoạt động" vào danh sách khuyến mãi trên trang chủ.
Khuyến mãi đang hoạt động là bản ghi có active: true; bản ghi không có trường này thì không hoạt động.
Kiểm tra với dữ liệu hiện tại: bật thì thấy A và C, tắt thì thấy A, B và C.
Đừng thêm tìm kiếm, sắp xếp hay bộ lọc khác; đó là các nhiệm vụ riêng.
Nếu dự án đã có công tắc tương tự, hãy làm theo cách đó
và cho biết bạn dựa trên công tắc nào.
Trước tiên, hãy mô tả trong một câu kịch bản bạn sẽ thực hiện,
rồi chờ tôi trả lời. Tạm thời đừng sửa tệp.

Hai dòng cuối không tốn công mà lại hữu ích: nếu agent diễn giải khác với ý bạn, bạn sẽ nhận ra trước khi mã bị sửa. Khi agent đưa ra kế hoạch, hãy hỏi ngắn gọn liệu giải pháp có quá phức tạp hoặc chưa đủ hay không, và vì sao. Câu hỏi này áp dụng cho mọi kế hoạch, đặc biệt hữu ích với feature vì những phần làm thêm thường ẩn ở đây. Cuối cùng, hãy kiểm tra cả hai trạng thái của công tắc và xem trên trang có xuất hiện thứ gì thừa không.

Sửa lỗi

Nút banner trên trang chủ cần mở trang /campaign/ có điều khoản khuyến mãi, nhưng hiện lại dẫn đến /old/.

Cách nhanh nhất với agent là điền thẳng /campaign/ vào liên kết của nút. Nếu địa chỉ vốn được ghi ngay trong liên kết thì cách này là đủ. Nhưng nếu địa chỉ lấy từ dữ liệu chung mà các nút khác cũng dùng, thay thế như vậy chỉ che đi triệu chứng: nút này sẽ hoạt động, nhưng lỗi trong dữ liệu vẫn còn. Một cách làm quen thuộc khác là tiện thể chỉnh lề và màu banner vì tệp đã mở sẵn.

Địa chỉ nằm trong dữ liệu chung: sửa liên kết của nút chỉ sửa được banner, nút khác vẫn dẫn đến /old/; sửa dữ liệu sẽ cập nhật địa chỉ cho cả hai nútSửa ở nútSửa dữ liệudata/promo.jsonurl: /old/Banner/campaign/Nút khác/old/data/promo.jsonurl: /campaign/Banner/campaign/Nút khác/campaign/
Sửa ở đâu tùy thuộc vào nơi nút lấy địa chỉ

Chỉ nhìn vào nút thì không biết bạn đang gặp trường hợp nào; phải xem mã mới rõ.

Trước khi yêu cầu agent làm gì, hãy tự tái hiện lỗi: nhấn nút và xem nó dẫn đến đâu. Nếu bạn không tái hiện được lỗi, đừng vội đóng nhiệm vụ; trước hết hãy xác định các bước và dữ liệu khi lỗi xuất hiện. Khi đã tái hiện được, bước tiếp theo là tìm nguyên nhân. Bạn chưa biết nút lấy địa chỉ từ đâu? Không sao, đó chính là câu hỏi đầu tiên dành cho agent:

Nút banner trên trang chủ dẫn đến /old/, nhưng cần dẫn đến /campaign/.
Trước tiên hãy tìm xem nút lấy địa chỉ từ đâu và chỉ cho tôi vị trí đó.
Tạm thời đừng sửa tệp.
Sau khi tôi xác nhận, hãy sửa địa chỉ ở nơi nó được thiết lập.
Đừng thay đổi giao diện banner.

Sau khi sửa, hãy lặp lại thao tác ban đầu: nhấn nút, kiểm tra địa chỉ và xác nhận điều khoản khuyến mãi hiển thị trên trang. Nếu bạn sửa dữ liệu địa chỉ dùng chung, hãy kiểm tra thêm một nơi khác đang sử dụng dữ liệu đó.

Thay đổi cấu trúc mã

Phần chuẩn bị dữ liệu cho bảng xếp hạng được viết hai lần gần như giống hệt nhau, nên mỗi lần chỉnh sửa đều phải làm ở cả hai chỗ. Mục tiêu là giữ lại một phiên bản mà không làm thay đổi bảng.

Điều cần được giữ nguyên sau khi sửa được gọi là bất biến (invariant). Ở đây, đó là thứ tự người tham gia và các con số trên bảng với cùng một bộ dữ liệu.

Lỗi do refactoring thường không gây ồn ào. Thứ tự khi bằng điểm bị đảo, cách làm tròn thay đổi, nhưng chẳng có thông báo lỗi nào; bảng chỉ đơn giản hiển thị sai. Sau khi gộp hai phiên bản, agent có thể báo "hành vi không thay đổi" dù chưa chạy gì cả. Hoặc tiện thể "cải thiện" cách sắp xếp vì thấy cách cũ kỳ lạ.

Hãy lấy bộ dữ liệu theo thứ tự này: A có 10 điểm, C có 20, B có 20, D có 15.5. Hiện tại bảng hiển thị C: 20, B: 20, D: 15.5, A: 10. Với refactoring, kết quả hiện tại của mã chính là chuẩn để đối chiếu, kể cả khi bạn muốn nó khác đi. Bộ dữ liệu này được chọn có chủ đích. C đứng trước B, vì vậy nếu phiên bản mới sắp xếp những người bằng điểm theo tên, B sẽ vượt lên trước. Giá trị thập phân cũng giúp nhận ra nếu cách làm tròn thay đổi.

Trước refactoring, dữ liệu đi qua hai bản sao của bước chuẩn bị; sau đó chỉ qua một phiên bản. Đầu vào và bảng kết quả C 20, B 20, D 15.5, A 10 vẫn như nhauTrướcSau=Đầu vào và bảng vẫn vậy, chỉ phần giữa thay đổiBộ dữ liệu A, B, C, DBộ dữ liệu A, B, C, DBản sao 1Bản sao 2Chuẩn bịmột phiên bảnBảngC 20, B 20, D 15.5, A 10BảngC 20, B 20, D 15.5, A 10
Bất biến là điều được giữ ở hai đầu sơ đồ

Mẫu yêu cầu dưới đây ngăn được cả hai kiểu trượt mục tiêu:

Phần chuẩn bị dữ liệu cho bảng xếp hạng bị lặp ở hai nơi, hãy giữ lại một phiên bản.
Đừng thay đổi hành vi của bảng, kể cả khi có điểm nào trông kỳ lạ;
hãy liệt kê riêng những điểm đó.
Trước khi sửa, hãy chạy các bài kiểm tra hiện có và xuất bảng
cho bộ dữ liệu A: 10, C: 20, B: 20, D: 15.5. Lưu lại cả hai kết quả.
Sau khi sửa, hãy làm lại như vậy và so sánh.
Trong báo cáo, hãy cho biết phần chuẩn bị dữ liệu hiện nằm ở đâu.

Hãy tự so sánh các kết quả đã lưu, đừng chỉ dựa vào dòng "khớp" trong báo cáo. Kết quả trùng nhau chỉ cho thấy hành vi được giữ nguyên với bộ dữ liệu này, không hơn. Dòng cuối của yêu cầu giúp kiểm tra đúng mục tiêu: bảng giống nhau chưa có nghĩa là phần lặp đã thực sự được loại bỏ.

Ở đây, làm nửa vời còn tệ hơn không làm. Giả sử một bản sao vẫn còn và một số chỗ gọi đã chuyển sang phiên bản mới. Bảng vẫn vậy, nhưng giờ có hai cách chuẩn bị dữ liệu; ở yêu cầu tiếp theo, agent có thể dựa trên cách cũ vì nó không biết cách nào mới là đúng. Đã bắt đầu gộp thì hãy hoàn tất để chỉ còn một phiên bản; nếu không kịp, hãy dừng sớm hơn và ghi rõ phần nào vẫn còn.

Tìm hiểu mà không sửa

Research khác biệt rõ nhất so với các loại còn lại: kết quả là một câu trả lời, không phải dự án đã thay đổi. Câu hỏi trong cổng thông tin của chúng ta là địa chỉ mà nút khuyến mãi dẫn đến được chọn ở đâu. Cũng chính câu hỏi này mở đầu cho việc sửa lỗi, nhưng ở đây không hề yêu cầu chỉnh sửa. Đó là nhiệm vụ kết hợp: trước tiên tìm hiểu, sau đó sửa ở một giai đoạn riêng.

Agent thường trượt mục tiêu theo hai cách. Thứ nhất là bắt đầu sửa ngay trong lúc tìm hiểu: thấy một đoạn có vấn đề, và sửa nó có vẻ nhanh hơn mô tả. Thế là việc tìm hiểu biến thành một lần sửa không có cả yêu cầu rõ ràng lẫn bước kiểm tra. Cách thứ hai khó nhận ra hơn. Agent đọc mã rồi tự tin kể lại trang hoạt động thế nào dù chưa chạy trang; người đọc khó phân biệt lời kể dựa trên mã với điều đã quan sát thực tế.

Cách trượt mục tiêu thứ ba bắt đầu từ chính yêu cầu. Nếu bạn viết điều định làm tiếp theo, agent sẽ điều chỉnh câu trả lời theo mục tiêu đó: thay vì mô tả hiện trạng, nó sẽ đưa ra nhận xét về cách làm tốt hơn. Vì vậy, trong research, hãy hỏi về cấu trúc hiện tại và để dự định của bạn cho giai đoạn sau. Yêu cầu bên dưới không hề nhắc đến chuyện sẽ sửa địa chỉ sau đó.

Tìm xem nút banner trên trang chủ lấy địa chỉ từ đâu. Đừng sửa gì cả.
Trong câu trả lời, hãy nêu: các tệp và vị trí trong mã, chúng liên hệ với nhau thế nào,
điều gì bạn xác định được qua việc đọc và điều gì qua việc chạy,
và những gì bạn vẫn chưa biết.

Một câu trả lời tốt cho yêu cầu này có thể như sau: "Trong pages/home.html, địa chỉ nút lấy từ promo.url; giá trị /old/ được đặt trong data/promo.json. Tôi xác định được điều này bằng cách đọc hai tệp. Tôi chưa chạy trang và chưa kiểm tra liệu script có thay đổi địa chỉ hay không." Mối liên hệ có thể kiểm chứng, và giới hạn hiểu biết được nêu rõ.

Nếu phát hiện chỗ đáng ngờ trong lúc tìm hiểu, hãy ghi riêng trong câu trả lời: bạn thấy gì và còn thiếu thông tin nào để kết luận. Việc sửa chữa thuộc giai đoạn tiếp theo, với yêu cầu riêng.

Dùng Plan để tìm hiểu

Plan phù hợp cho việc tìm hiểu vì không sửa mã nguồn (trừ khi trong phiên terminal bypassPermissions được cho phép; khi đó không có cơ chế chặn, ngay cả khi bạn chưa bật chế độ này). Các lệnh chỉ đọc chạy mà không cần hỏi, những lệnh khác sẽ yêu cầu quyền hoặc được kiểm tra tự động, vì vậy vẫn hãy xem kỹ lệnh sắp chạy. Không cần phê duyệt kế hoạch ở cuối; việc phê duyệt sẽ bắt đầu sửa mã. Hãy lấy câu trả lời rồi thoát Plan bằng cách nhấn Shift+Tab cho đến khi thanh trạng thái hiển thị manual mode on. Nếu bạn dùng được chế độ auto, trong vòng chuyển này nó có thể đứng ngay sau Plan.

Bước đầu tiên và ranh giới

Tất cả được tóm tắt trong một bảng.

LoạiCâu hỏi đầu tiênAgent sẽ tự làm gìCần kiểm tra gì
FeatureNgười dùng cần thao tác mới nào?thêm tiện ích liên quan, tự quyết định về dữ liệukịch bản đã nêu hoạt động, không có phần thừa
BugfixĐiều gì đang xảy ra thay vì kết quả mong đợi?sửa triệu chứng hoặc tiện thể chỉnh giao diệnkịch bản ban đầu cho ra kết quả cần có
RefactoringĐiều gì cần được giữ nguyên?báo cáo mà không chạy thử, "cải thiện" hành vi hoặc bỏ dở giữa chừngkết quả trước và sau khớp nhau, phần lặp được loại bỏ hoàn toàn
ResearchHiện tại nó được tổ chức ra sao?bắt đầu sửa hoặc nói như thể đã quan sát dù chỉ đọc mãcâu trả lời nêu tệp, căn cứ và điều chưa biết

Cột thứ ba là những gì bạn nên chặn rõ trong yêu cầu. Cột thứ tư là những gì bạn nên tự kiểm tra hoặc yêu cầu agent trình bày trong câu trả lời.

Mặt khác, tên loại nhiệm vụ không tự nó cho phép làm mọi thứ. "Chỉ là refactoring thôi" không phải lý do để viết lại nửa mô-đun; "chỉ là bugfix" cũng không có nghĩa là được tiện thể đổi giao diện. Loại nhiệm vụ gợi ý câu hỏi và bước kiểm tra, còn ranh giới vẫn do bạn xác định.

Viết lại sáu yêu cầu

Dưới đây là sáu yêu cầu như cách người ta thường viết. Với mỗi yêu cầu, hãy xác định loại và viết lại sao cho agent ít phải tự suy diễn hơn: kết quả, ranh giới, cách kiểm tra. Phần phân tích nằm ngay bên dưới, vậy hãy tự làm thử trước nhé.

  1. "Thêm chức năng xuất bảng xếp hạng sang CSV".
  2. "Thỉnh thoảng bảng xếp hạng hiển thị một người tham gia hai lần, hãy sửa đi".
  3. "Tách pages/home.html thành nhiều phần, tệp này lớn quá".
  4. "Vì sao trang chủ tải chậm vậy?"
  5. "Sửa liên kết và tiện thể viết lại giao diện".
  6. "Làm cho cổng thông tin tốt hơn".

Một cách phân tích có thể là như sau. Cách diễn đạt của bạn có thể khác; điều quan trọng là vẫn giữ được những điểm tựa này.

1. Xuất bảng xếp hạng.
Feature. Còn thiếu kịch bản: cột nào sẽ có trong tệp, các dòng theo thứ tự nào và nút xuất dữ liệu nằm ở đâu. Cũng có một câu hỏi về dữ liệu rất dễ bỏ qua: những người chưa có điểm có được đưa vào tệp không? Hãy tự quyết định, nếu không agent sẽ quyết định thay bạn. Hãy nêu riêng những gì không cần làm: nhập dữ liệu, chọn cột, các định dạng khác. Cách kiểm tra khá đơn giản: xuất tệp với bộ dữ liệu A, B, C, D rồi đối chiếu các dòng với bảng trên màn hình.

2. Một người xuất hiện hai lần.
Bugfix, nhưng từ quan trọng ở đây là "thỉnh thoảng", không phải "hãy sửa đi". Khi chưa biết lúc nào bản ghi trùng xuất hiện, agent chưa có gì cụ thể để sửa; nhiều khả năng nó sẽ loại bỏ các dòng trùng trước khi hiển thị bảng. Đó chính là triệu chứng bị che giấu. Trước tiên, hãy tìm hiểu sau thao tác nào và với dữ liệu nào dòng bị nhân đôi, rồi yêu cầu tìm vị trí xảy ra lỗi mà chưa sửa gì. Cách kiểm tra là lặp lại chính kịch bản từng gây ra lỗi trùng.

3. Tách tệp lớn.
Refactoring, nhưng bất biến chưa được nêu và cũng chưa có cách kiểm tra "không có gì bị hỏng". Hãy bổ sung rằng trang cần trông và hoạt động như trước - banner, danh sách khuyến mãi, bảng xếp hạng - đồng thời ghi lại kết quả kiểm tra trước khi sửa. Cũng nên chặn phần dễ làm thêm: khi tách markup sang các tệp khác, agent có thể tiện thể chỉnh lề hoặc đổi tên class.

4. Trang tải chậm.
Research: đây là câu hỏi, không phải yêu cầu tăng tốc. Nhưng agent dễ hiểu thành "làm cho nhanh hơn"; nếu không nói "đừng thay đổi gì", câu trả lời có thể là tối ưu hóa theo phỏng đoán. Trong câu trả lời, hãy yêu cầu phân biệt điều đã đo với điều chỉ suy ra: "ảnh có dung lượng vài megabyte" là sự thật nếu agent đã kiểm tra, còn "có lẽ script làm chậm" chỉ là phỏng đoán. Sửa lỗi là giai đoạn tiếp theo, khi đã biết nguyên nhân.

5. Sửa liên kết và viết lại giao diện.
Một yêu cầu nhưng có hai kết quả. Sửa liên kết là bugfix. Giao diện mới không thực sự khớp với bốn loại: hành vi không đổi, chỉ thay đổi hình thức. Gần nhất là feature, nhưng kết quả được xác định bằng mẫu - mockup hoặc ảnh chụp màn hình - chứ không phải kịch bản. Trước tiên hãy sửa lỗi bằng yêu cầu ở phần bugfix; sau khi kiểm tra xong mới làm giao diện:

Bây giờ hãy chỉnh giao diện banner theo mockup @banner-mockup.png.
Đừng thay đổi địa chỉ nút hoặc nội dung văn bản.

6. Làm cổng thông tin tốt hơn.
Chưa thể xác định loại nhiệm vụ, và cũng không cần đoán. Hãy hỏi người đưa ra yêu cầu điều gì đang gây trở ngại. Nếu chính bạn là người yêu cầu, hãy tự hỏi mình. Bạn cũng có thể nhờ agent làm rõ:

Trước khi thay đổi bất cứ điều gì, hãy hỏi tôi tối đa năm câu,
những câu cần thiết để hiểu chính xác điều gì cần cải thiện.
Đừng sửa tệp.

Thông thường, loại nhiệm vụ sẽ tự hiện rõ qua các câu trả lời.