Free

Membiarkan Claude Melakukan Refactor

Refactor tidak bergejala — kesalahan Claude tak terlihat. Tiga pengaman: test, commit atomik, klik manual.


Refactor adalah tugas di mana Claude paling berbahaya. Bug punya gejala — tombol tidak bereaksi, nilai undefined, stack trace — jadi kamu bisa tahu apakah perbaikan Claude benar. Refactor tidak. "Masih jalan" bisa berarti "test masih pass" sementara perilaku diam-diam berubah, kamu tidak sadar, dan production meledak seminggu kemudian.

Baru-baru ini saya melakukan refactor yang cukup besar dengan Claude di how2claude: memigrasi pembayaran kripto x402 dari PaymentHandler + FacilitatorClient buatan sendiri (139 baris) ke gem x402-rails, dan sekaligus mengekstrak pemetaan field Purchase.create! / Subscription.create! — yang terduplikasi di dua controller — ke method kelas model. Satu commit, 4 file diubah, 2 dihapus, 2 ditambah.

Prompt saya satu kata: "refactor."

Bisa sesingkat itu karena ada pengaman di sekitarnya.

Pengaman #1: Jangan refactor tanpa test

Saat branch ini sampai ke titik itu, ada 221 test. Semua path kritis di alur pembayaran tertutup.

Tindakan default Claude sebelum refactor bukan "lihat test dulu." Jadi saya suruh dia jalankan bin/rails test dulu, pastikan hijau, baru menyentuh apa pun.

Setelah refactor, jalankan lagi. Masih hijau. Itu bukan berarti tidak ada regresi — itu berarti perilaku yang diketahui tidak rusak.

Kalau codepath kamu tidak punya cakupan test, suruh Claude tulis test minimal yang mengunci perilaku sekarang. Jalankan, commit. Setelah itu refactor. Kalau tidak, yang dia lakukan bukan refactor, itu rewriting — dan kamu tidak punya cara memverifikasi ekivalensi.

Pengaman #2: Suruh dia pecah perubahan jadi commit atomik

Refactor ini sebenarnya dua hal:

  1. Backend x402: buatan sendiri → gem
  2. Pemetaan field Purchase / Subscription: controller → method kelas model

Ada yang ketiga di frontend: menulis ulang alur signing sisi JS dengan viem + x402-fetch.

Saya suruh Claude pecah berdasarkan batas alami: backend + ekstraksi model satu commit (9f3e239), frontend commit terpisah (93746d8). Setiap commit membawa deskripsi lengkap, daftar file, dan alasan perubahannya.

Keuntungan:
- Diff tetap mudah dibaca. Satu commit, satu hal.
- Granularitas rollback. Kalau production kena bug frontend, git revert 93746d8 hanya rollback frontend, backend tetap.
- Perhatian Claude sendiri tetap fokus. Satu commit satu hal — perhatiannya juga hanya meliputi hal itu.

Pengaman #3: Baca diff sebelum bilang selesai

Setelah refactor selesai, saya suruh Claude berhenti dan tunjukkan git diff --staged. Jangan jalankan test. Jangan jalankan app. Baca diff dulu.

Sinyal yang saya pindai:

  • Apa yang dia hapus? app/services/x402/payment_handler.rb dihapus seluruhnya — oke, itu tujuan dari migrasi ke gem. Tapi kalau dia menghapus sesuatu yang tidak saya minta disentuh, saya berhenti dan tanya.
  • Apakah pemetaan field berubah? Purchase.create!(wallet_address: verify_result["payer"], ...)Purchase.record_x402!(payment:, settlement:) sekarang membaca payment[:payer]. Sumbernya berubah (request.env dari gem vs return value client lama), tapi field-nya harus dipetakan satu-satu.
  • Perubahan "sambil lalu." Claude sangat suka memperbaiki hal yang "kelihatannya salah" sambil refactor — memperbaharui pesan error, mengubah nama variabel, mengekstrak method yang menurutnya harus ada. Awasi ini. Janji refactor adalah "ekivalen perilaku." Perubahan sambil lalu melanggar itu.

Dua hal yang Claude lakukan salah kali ini

Jebakan 1: Stimulus controllers gem diam-diam tidak ter-load

Gem x402-rails membawa Stimulus controllers sendiri. Claude menulis kode, test hijau semua. Saya klik manual tombol bayar — tidak bereaksi.

Alasan: config/importmap.rb punya pin untuk @hotwired/stimulus yang menunjuk ke file vendor yang tidak ada, dan importmap diam-diam membuang pin itu. Controllers dari gem tidak pernah ter-load. Test tidak bisa menangkap ini karena bin/rails test tidak mengeksekusi JS.

Jebakan 2: YAML mem-parsing 0x... sebagai integer

wallet_address: 0x833589... — tanpa kutip. YAML melihat awalan 0x, membacanya sebagai integer heksadesimal. Facilitator menerima non-string dan menolak. Claude tidak berhenti memikirkan aturan parsing YAML saat menulis config.

Kedua jebakan ketangkap karena saya meng-klik tombol beneran. Test pass bukan sama dengan fitur bekerja. Verifikasi manual setelah refactor tidak opsional.

Alur refactor lengkap dengan Claude

  1. Jalankan seluruh test suite. Konfirmasi hijau. Kalau kode target tidak punya cakupan, suruh Claude tulis test minimal yang mengunci perilaku sekarang, lalu commit itu.
  2. Katakan permukaan apa yang mau diekstrak. "Refactor" cukup ketika duplikasinya jelas; kalau tidak, katakan "ekstrak pemetaan field dari X ke method kelas di Y."
  3. Pecah jadi commit atomik. Satu commit, satu hal.
  4. Baca diff sendiri. Apa yang dihapus, apakah pemetaan field masih cocok, apakah ada perubahan sambil lalu.
  5. Jalankan test lagi. Hijau.
  6. Kalau menyentuh path yang terlihat user, klik fitur secara manual. Lapisan yang tidak bisa dicapai test — JS, importmap, CDN, parsing YAML — harus kamu lihat dengan mata sendiri.

Refactor adalah skenario "biarkan Claude yang nyetir" dengan risiko tertinggi. Pengaman bukan untuk Claude. Itu untukmu — supaya saat Claude salah, kamu menangkapnya dalam 5 menit, bukan saat kebakaran di production.