Free

Khiến Claude đổi ý

Đề xuất "nhẹ hơn" của Claude ẩn chứa chi phí thật. Một câu hỏi ngược đủ để phơi bày tất cả.


Tôi đang sửa smarts — một app Rails render smart contract Ethereum thành "tài liệu cho lập trình viên". Mấy hôm trước đụng phải vấn đề cũ: contract sẽ được upgrade. Nếu hệ thống render một proxy contract đã upgrade bằng ABI lỗi thời, nghĩa là nó đang lừa user.

Tôi nhờ Claude thiết kế cùng cơ chế phát hiện upgrade proxy. Nó cho ra một kế hoạch kỹ thuật khá đầy đủ, gồm tương thích EIP-1967, Beacon proxy và ZeppelinOS. Đến bước "khi nào trigger việc kiểm tra", nó liệt kê hai phương án:

# Phương án A: thêm last_proxy_check_at vào DB, throttle 5 phút
if @contract.last_proxy_check_at.nil? || @contract.last_proxy_check_at < 5.minutes.ago
  CheckProxyUpgradeJob.perform_later(@contract)
end

# Phương án B: throttle qua Solid Cache, nhẹ hơn
unless Rails.cache.exist?("proxy_check:#{@contract.id}")
  Rails.cache.write("proxy_check:#{@contract.id}", true, expires_in: 5.minutes)
  CheckProxyUpgradeJob.perform_later(@contract)
end

Câu kết của nó:

B đơn giản hơn, không cần thêm cột.

Tôi nhìn hai đoạn code một lúc. B đúng là đơn giản hơn, không migration, toàn cache call, rất kiểu Rails. Nhưng có gì đó không khớp.

Tôi gõ năm ký tự rồi enter:

nên a?

Claude tính lại

Câu trả lời (đã cắt vài block code):

Đổi ý — chọn A.

Lý do: toàn bộ mục đích của việc phát hiện proxy là không tin vào độ tươi của ABI. Nếu throttle qua Solid Cache, ta đang đặt lại "đã kiểm tra chưa?" vào storage dễ mất:

  • Sau deploy / cache wipe, toàn bộ contract cùng lúc rơi vào trạng thái "chưa kiểm tra" → đợt traffic kế tiếp kích hoạt bão kiểm tra
  • Không có cách trả lời "contract này lần cuối được kiểm tra cách đây bao lâu?"
  • Không làm được lưới an toàn theo lịch: contract ít người xem có thể hàng tuần không có traffic, lazy trigger sẽ không bao giờ chạy

Thêm cột last_proxy_check_at mở khóa được:

  1. Lazy trigger (cùng logic với B)
  2. Lưới an toàn theo lịch qua recurring.yml: enqueue theo lô các contract quá 24h — độc lập với traffic người dùng
  3. Hiện độ tươi trên UI: "Implementation verified 12 min ago"

Chi phí: một migration + một update_column mỗi job (không qua callback, gần như miễn phí).

Tóm lại: B trông "Rails-native hơn, nhẹ hơn", nhưng thiếu hẳn một lớp năng lực. A mới là phương án thực sự thực thi điều "ta không tin độ tươi của ABI".

Để ý cái xảy ra giữa hai lần khuyến nghị:

  • Lần đầu: liệt kê phương án → so khối lượng code → chọn B ("đơn giản hơn")
  • Lần hai: neo vào vì sao ta làm việc này → nhận ra B đặt state vào storage dễ mất, mâu thuẫn với tiền đề "không tin độ tươi của ABI" → chọn A

Giữa hai lần đó không có dữ kiện mới nào vào. Khối lượng code, idiom Rails, semantics của cache — y nguyên. Cái thay đổi chỉ là nó đứng ở lớp nào khi đánh giá.

Lần đầu so khối lượng code. Lần hai so "thay đổi này có thật sự giải quyết bài toán ban đầu không?".

Năm ký tự đó đã làm gì

Khi gõ "nên a?", tôi không định ép nó về đâu. Chỉ là kẹt ở code của B, theo bản năng hỏi lại sở thích của nó.

Nhưng hiệu ứng là kéo nó ra khỏi mode "chọn một trong hai phương án" về lại "vì sao ta làm việc này?". Khoảng cách đó là cốt lõi.

Khi LLM ra khuyến nghị, gần như chúng luôn xuất phát từ local optima: diff nào nhỏ hơn, snippet nào idiomatic hơn, implement nào đọc sạch hơn. Đều là những trục thật. Nhưng chúng thường không kết nối với PR này thực sự để làm gì.

A và B tương đương trên trục "throttle 5 phút". Nhưng A còn mở khóa thêm ba thứ B không làm được — lưới theo lịch, observability, độ tươi trên UI. Nếu chỉ nhìn lớp throttle, B thắng. Nếu quan tâm vì sao job này tồn tại, A thắng.

Claude không thấy lớp đó ở lần đầu không phải vì nó không thấy được. Là vì khung mặc định của "so các phương án" là lớp cú pháp/Rails. Một câu hỏi ngược kéo nó lên lớp bài toán, và từ đó nó tự tính ra chi phí của B.

Biến thành thói quen

Vài điều chỉnh nhỏ trong workflow của tôi:

Khi Claude kết bằng "đơn giản hơn / nhẹ hơn / native hơn", nhìn thêm lần nữa. Đó là từ thẩm mỹ, không phải từ phán xét. Chúng nói về cái dễ đọc, không phải cái chịu được áp lực.

Chi phí hỏi lại bằng không. "nên X?" — năm ký tự. Claude buộc phải đánh giá từ một góc nó chưa đứng. Dù cuối cùng vẫn ra khuyến nghị cũ, lập luận sẽ chắc hơn — đủ để bạn quyết định có theo không.

Bắt nó trải chi phí ra, đừng chỉ liệt kê khuyến nghị. Lần này thành công không phải do tôi ép A — mà do Claude tự viết ra các chi phí ẩn của B (bão sau cache wipe, thiếu observability, ngõ cụt cho việc theo lịch). Trước khi nó viết, tôi cũng chưa diễn đạt được chúng.

Cuối cùng tôi triển A: thêm cột last_proxy_check_at, lazy trigger cộng lưới 24h theo lịch, UI giờ có thêm dòng Implementation verified 12 min ago. Ba ngày sau, làm việc khác không liên quan, cột đó cho phép tôi viết Contract.where("last_proxy_check_at < ?", 24.hours.ago).find_each — query mà nếu chọn B thì đơn giản không tồn tại được.

Năm ký tự hỏi ngược, giá trị tương đương khoảng một lần refactor một tuần sau.