Biết nguyên nhân gốc chưa đủ; agent cần đề xuất bản sửa đúng chỗ, nhỏ, kiểm chứng được và chứng minh nó thực sự sửa lỗi mà không phá thứ khác. Đây là nơi phân biệt agent "nghe hay" với agent "làm được".
Khung tư duy: Fix theo test trước (regression-first)
Quy trình vàng:
- Viết một test tái hiện lỗi — test này phải FAIL trên code hiện tại.
- Áp bản fix.
- Test chuyển PASS.
- Chạy toàn bộ test cũ để bảo đảm không hồi quy.
Ví dụ cụ thể
Nguyên nhân gốc từ Bài 4: thiếu index. Agent đề xuất:
def test_export_uses_index():
plan = db.explain("SELECT * FROM orders WHERE created_at >= %s", cutoff)
assert "Seq Scan" not in plan # FAIL trước khi thêm index
-- Fix: migration thêm index
CREATE INDEX CONCURRENTLY idx_orders_created_at ON orders (created_at);
CONCURRENTLY để không khóa bảng khi tạo index trên production — một chi tiết an toàn agent phải biết nêu ra.
Các bước đề xuất fix
- Chọn phạm vi tối thiểu: sửa đúng nguyên nhân gốc, không "nhân tiện" refactor.
- Viết regression test khiến lỗi lộ ra (fail trước fix).
- Sinh diff cho bản fix, kèm giải thích tại sao nó sửa nguyên nhân gốc.
- Đánh giá tác động (blast radius): fix chạm những gì? có migration/rollback không?
- Chạy test đầy đủ + fix; báo cáo trước/sau.
BÁO CÁO FIX
- Root cause : thiếu index created_at -> Seq Scan
- Fix : CREATE INDEX CONCURRENTLY ...
- Test mới : test_export_uses_index (đỏ -> xanh)
- Regression : 428/428 test cũ vẫn xanh
- Rollback : DROP INDEX (an toàn, không mất data)
- Rủi ro : tạo index tốn ~2 phút, dùng CONCURRENTLY nên không khóa
Nhiều fix, chọn cái nào
Agent nên trình bày đánh đổi: fix nhanh (tăng timeout gateway) vs fix gốc (thêm index). Ghi rõ fix nhanh chỉ là giảm đau tạm thời, fix gốc mới bền. Quyết định cuối để con người duyệt.
Checklist trước khi đề xuất merge
[ ] Có regression test fail-trước / pass-sau
[ ] Toàn bộ test cũ vẫn xanh
[ ] Diff nhỏ, đúng phạm vi nguyên nhân gốc
[ ] Có kế hoạch rollback
[ ] Đã ghi rủi ro & blast radius
[ ] Không kèm refactor không liên quan
Sai lầm thường gặp
- Fix không có test tái hiện → không ai chắc lỗi đã hết, dễ tái phát.
- Sửa lan man/refactor kèm theo → review khó, tăng rủi ro hồi quy.
- Chỉ chạy test mới, quên test cũ → sửa được A, phá B.
- Không có kế hoạch rollback → khi fix gây sự cố, không lùi nhanh được.
- Nhầm giảm đau (workaround) là fix gốc → nợ kỹ thuật tích lại, lỗi quay lại.
<!--les-append-->
Blast radius: cách đánh giá cụ thể
"Fix này chạm những gì" dễ trả lời hời hợt. Bốn câu cụ thể, và câu cuối là câu hay bị bỏ:
| Câu hỏi | Vì sao quan trọng |
|---|---|
| Fix chạm mã dùng chung hay mã cục bộ? | Mã dùng chung thì phạm vi là mọi nơi gọi nó |
| Có migration dữ liệu không? | Migration là loại thay đổi khó hoàn tác nhất |
| Có đổi hành vi mà bên ngoài đang dựa vào? | Sửa một lỗi mà ai đó đã viết code để lách nó |
| Fix có đổi hiệu năng ở đường nóng? | Thêm một phép kiểm vào vòng lặp chạy triệu lần |
Fix nhanh và fix gốc: quyết định như thế nào
Đừng coi fix nhanh là xấu. Nó đúng trong một tình huống cụ thể: khi đang chảy máu. Khung quyết định:
| Tình huống | Làm gì |
|---|---|
| Đang gây thiệt hại cho người dùng ngay lúc này | Fix nhanh ngay, kèm ticket fix gốc có ngày |
| Lỗi có nhưng không cấp bách | Fix gốc luôn, đừng qua bước tạm |
| Fix gốc cần migration lớn | Fix nhanh + kế hoạch fix gốc, ghi rõ nợ |
Bắt agent chứng minh test thật sự bắt được lỗi
Một test pass sau fix không chứng minh gì — nó có thể pass cả trước fix. Bước kiểm bắt buộc:
- Áp test trước khi áp fix. Nó phải FAIL.
- Áp fix. Test PASS.
- Nếu bước 1 không fail, test đang kiểm sai thứ.
Một dòng cần có trong mọi báo cáo fix
Thêm dòng này vào mẫu báo cáo ở trên:
> Vì sao lỗi này lọt qua được: không có test nào chạy trên dữ liệu trên 10.000 dòng.
Dòng đó là dòng có giá trị dài hạn nhất trong cả báo cáo, vì nó chỉ ra lỗ hổng trong lưới an toàn — thứ sẽ để lọt lỗi tiếp theo nếu không ai vá. <!--/les-append-->