Rekomendasi "lebih ringan" dari Claude menyembunyikan biaya nyata. Satu pertanyaan balik cukup untuk membongkarnya.
Saya sedang menggarap smarts — aplikasi Rails yang me-render smart contract Ethereum sebagai "dokumentasi developer". Beberapa hari lalu saya kembali bertemu masalah lama: contract bisa di-upgrade. Kalau sistem me-render proxy contract yang sudah di-upgrade pakai ABI usang, sistem itu sedang membohongi pengguna.
Saya minta Claude mendesain bareng mekanisme deteksi upgrade proxy. Dia kasih plan teknis lumayan lengkap, termasuk kompatibilitas EIP-1967, Beacon proxy, dan ZeppelinOS. Sampai bagian "kapan trigger pengecekan", dia sodorkan dua opsi:
# Opsi A: tambah last_proxy_check_at ke DB, throttle 5 menit
if @contract.last_proxy_check_at.nil? || @contract.last_proxy_check_at < 5.minutes.ago
CheckProxyUpgradeJob.perform_later(@contract)
end
# Opsi B: throttle via Solid Cache, lebih ringan
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
Kalimat penutupnya:
B lebih simpel, tidak perlu nambah kolom.
Saya menatap dua snippet itu sebentar. B memang lebih simpel — tanpa migration, semua via cache, sangat Rails. Tapi ada yang ganjil.
Saya ketik lima karakter dan tekan enter:
rekomen a?
Balasannya (beberapa code block dipangkas):
Berubah pikiran — rekomendasi A.
Alasannya: seluruh tujuan deteksi proxy adalah tidak mempercayai kesegaran ABI. Kalau throttle pakai Solid Cache, kita malah balik menyimpan "sudah dicek belum?" di storage volatil:
- Setelah deploy / cache wipe, semua contract serentak masuk state "belum dicek" → gelombang trafik berikutnya memicu badai pengecekan
- Tidak ada cara menjawab "sudah berapa lama contract ini tidak diverifikasi ulang?"
- Tidak bisa membuat safety net terjadwal: contract dingin bisa berminggu-minggu tanpa trafik dan lazy trigger tidak akan pernah jalan
Yang dibuka oleh kolom
last_proxy_check_at:
- Lazy trigger (logika sama dengan B)
- Safety net terjadwal via recurring.yml yang batch-enqueue contract di atas 24 jam — independen dari trafik pengguna
- Tampilkan kesegaran di UI: "Implementation verified 12 min ago"
Biaya: satu migration + satu
update_columnper job (tanpa callback, praktis gratis).Singkatnya: B kelihatan "lebih Rails-native, lebih ringan", tapi kekurangan satu lapis kemampuan. A adalah opsi yang benar-benar mewujudkan "kita tidak percaya kesegaran ABI".
Perhatikan apa yang terjadi di antara dua rekomendasi itu:
Tidak ada fakta baru yang masuk di antara dua pass itu. Volume kode, idiom Rails, semantik cache — semua tetap. Yang berubah hanya dari lapisan mana dia mengevaluasi.
Pass pertama membandingkan volume kode. Pass kedua membandingkan "apakah perubahan ini benar-benar menyelesaikan masalah yang kita angkat?".
Waktu saya ketik "rekomen a?", saya tidak sedang memaksanya ke arah mana pun. Saya cuma macet di kode B dan refleks bertanya ulang preferensinya.
Tapi efeknya adalah menariknya keluar dari mode "pilih satu dari dua opsi" balik ke "kenapa kita melakukan ini?". Selisih itu yang penting.
Kalau LLM kasih rekomendasi, mereka hampir selalu mulai dari local optima: diff mana yang lebih kecil, snippet mana yang lebih idiomatis, implementasi mana yang lebih bersih dibaca. Itu sumbu nyata. Tapi sumbu-sumbu itu sering tidak nyambung dengan PR ini sebenarnya buat apa.
A dan B setara di sumbu "throttle 5 menit". Tapi A juga membuka tiga hal yang B tidak bisa — safety net terjadwal, observability, kesegaran di UI. Kalau cuma lihat lapisan throttle, B menang. Kalau peduli kenapa job ini ada, A menang.
Claude tidak melihat lapisan itu di pass pertama bukan karena tidak bisa. Itu karena frame default untuk "bandingkan opsi-opsi ini" adalah lapisan sintaks/Rails. Satu kontra-pertanyaan menariknya ke lapisan masalah, dan dari sana dia menghitung sendiri biaya B.
Beberapa penyesuaian kecil di workflow saya:
Kalau Claude menutup dengan "lebih simpel / lebih ringan / lebih native", lihat dua kali. Itu kata estetis, bukan kata penghakiman. Mereka menggambarkan apa yang nyaman dibaca, bukan apa yang tahan banting.
Biaya bertanya ulang nol. "rekomen X?" — lima karakter. Claude dipaksa mengevaluasi dari sudut yang belum dia ambil. Bahkan kalau dia mendarat di rekomendasi yang sama, alasannya akan lebih kokoh — cukup buat kamu memutuskan apakah mau ikut.
Suruh dia menjabarkan biaya, bukan cuma rekomendasi. Yang membuat kali ini berhasil bukan saya memaksa A — tapi Claude sendiri menuliskan biaya tersembunyi B (badai setelah cache wipe, ketiadaan observability, jalan buntu untuk pekerjaan terjadwal). Sebelum dia tulis, saya sendiri belum mengartikulasikan satu pun.
Akhirnya saya rilis A: tambah kolom last_proxy_check_at, lazy trigger plus safety net 24 jam, dan UI sekarang menampilkan baris Implementation verified 12 min ago. Tiga hari kemudian, saat mengerjakan hal lain yang tidak terkait, kolom itu memungkinkan saya menulis Contract.where("last_proxy_check_at < ?", 24.hours.ago).find_each — query yang dengan B sama sekali tidak akan ada.
Lima karakter pertanyaan balik itu nilainya kira-kira setara dengan satu refactor seminggu kemudian.