Free

ให้ Claude ทำ refactor

Refactor ไม่มีอาการ — ไม่เห็นตอน Claude ผิด สามรั้ว: เทส, commit อะตอม, คลิกด้วยมือ


Refactor คืองานที่ Claude อันตรายที่สุด บั๊กมีอาการ — ปุ่มไม่ตอบสนอง ค่าเป็น undefined มี stack trace — คุณบอกได้ว่าแก้ถูกหรือไม่ Refactor ไม่มีอาการ "ยังรันได้" อาจหมายถึง "เทสยังผ่าน" ขณะที่พฤติกรรมเปลี่ยนไปเงียบ ๆ คุณไม่รู้ตัว แล้วอีกหนึ่งสัปดาห์ production ก็ระเบิด

เพิ่งทำ refactor ที่ค่อนข้างใหญ่กับ Claude บน how2claude: ย้ายการจ่าย crypto x402 จาก PaymentHandler + FacilitatorClient ที่เขียนเองล้วน ๆ (139 บรรทัด) ไปเป็น gem x402-rails พร้อมกันก็ดึงการ map field ของ Purchase.create! / Subscription.create! — ที่ซ้ำอยู่ใน controller สองตัว — ออกมาเป็น class method ของ model หนึ่ง commit 4 ไฟล์เปลี่ยน 2 ไฟล์ลบ 2 ไฟล์เพิ่ม

Prompt ของผมคำเดียว: "refactor"

สั้นขนาดนั้นได้เพราะมีรั้วกั้นล้อมรอบ

รั้ว #1: ห้าม refactor โดยไม่มีเทส

ตอนที่ branch มาถึงจุดนี้ มี 221 เทส ทุกเส้นทางสำคัญของ flow การจ่ายถูกคลุมหมด

การกระทำ default ของ Claude ก่อน refactor ไม่ใช่ "ดูเทสก่อน" ดังนั้นผมสั่งให้มันรัน bin/rails test ก่อน ยืนยันว่าเขียว แล้วค่อยแตะอะไร

หลัง refactor รันอีกครั้ง ยังเขียว นี่ไม่ได้หมายความว่าไม่มี regression — หมายความว่าพฤติกรรมที่รู้จักยังไม่พัง

ถ้า codepath ของคุณไม่มีการคลุมเทส ให้ Claude เขียนเทสขั้นต่ำที่ล็อกพฤติกรรมปัจจุบันไว้ก่อน รัน commit แล้วค่อย refactor ไม่อย่างนั้นสิ่งที่มันทำไม่ใช่ refactor แต่เป็น rewrite — และคุณไม่มีทางยืนยันความเท่ากัน

รั้ว #2: ให้มันตัดการเปลี่ยนแปลงเป็น commit อะตอม

Refactor นี้จริง ๆ คือสองเรื่อง:

  1. Backend x402: เขียนเอง → gem
  2. Map field ของ Purchase / Subscription: controller → class method ของ model

ฝั่ง frontend มีเรื่องที่สาม: เขียน flow การเซ็นฝั่ง JS ใหม่ด้วย viem + x402-fetch

ผมให้ Claude ตัดตามขอบเขตธรรมชาติ: backend + การดึง model เป็น commit เดียว (9f3e239), frontend เป็น commit แยก (93746d8) แต่ละ commit มีคำอธิบายครบ รายการไฟล์ และเหตุผลของการเปลี่ยน

ประโยชน์:
- diff อ่านได้ หนึ่ง commit หนึ่งเรื่อง
- ความละเอียดของ rollback ควบคุมได้ ถ้า production เจอบั๊ก frontend git revert 93746d8 ย้อนเฉพาะ frontend backend ยังอยู่
- ความสนใจของ Claude เองก็โฟกัส หนึ่ง commit หนึ่งเรื่อง — ความสนใจมันก็ครอบแค่เรื่องเดียว

รั้ว #3: อ่าน diff ก่อนบอกว่าเสร็จ

พอ refactor เสร็จ ผมให้ Claude หยุดและโชว์ git diff --staged อย่าเพิ่งรันเทส อย่าเพิ่งรันแอป อ่าน diff ก่อน

สัญญาณที่ผมสแกน:

  • มันลบอะไร? app/services/x402/payment_handler.rb ลบทั้งไฟล์ — โอเค นั่นคือจุดประสงค์ของการย้ายไป gem แต่ถ้ามันลบสิ่งที่ผมไม่ได้สั่งแตะ ผมหยุดและถามทันที
  • Map field เปลี่ยนไหม? Purchase.create!(wallet_address: verify_result["payer"], ...)Purchase.record_x402!(payment:, settlement:) ตอนนี้อ่าน payment[:payer] แหล่งเปลี่ยน (request.env ของ gem vs ค่า return ของ client เก่า) แต่ field ต้อง map หนึ่งต่อหนึ่ง
  • การเปลี่ยน "ระหว่างทาง" Claude ชอบแก้ของที่ "ดูเหมือนผิด" ระหว่าง refactor — เปลี่ยนถ้อยคำ error message เปลี่ยนชื่อตัวแปร ดึงเมธอดที่มันคิดว่าควรมี ระวังพวกนี้ สัญญาของ refactor คือ "พฤติกรรมเทียบเท่า" แก้ระหว่างทางทำให้สัญญาเสีย

สองกับดักที่ Claude ตกครั้งนี้

กับดัก 1: Stimulus controllers ของ gem โหลดไม่ขึ้นแบบเงียบ ๆ

gem x402-rails มาพร้อม Stimulus controllers ของตัวเอง Claude เขียนโค้ด เทสเขียวทั้งหมด ผมคลิกปุ่มจ่ายด้วยมือ — ไม่มีอะไรเกิดขึ้น

เหตุผล: ใน config/importmap.rb pin สำหรับ @hotwired/stimulus ชี้ไปที่ไฟล์ vendor ที่ไม่มีอยู่ และ importmap ทิ้ง pin นั้นอย่างเงียบ ๆ controllers ของ gem ไม่เคยถูกโหลด เทสจับไม่ได้ เพราะ bin/rails test ไม่รัน JS

กับดัก 2: YAML parse 0x... เป็น integer

wallet_address: 0x833589... — ไม่มีเครื่องหมายคำพูด YAML เห็น prefix 0x อ่านเป็นจำนวนเต็มฐาน 16 Facilitator ได้ค่าที่ไม่ใช่ string และปฏิเสธ Claude ไม่ได้หยุดคิดถึงกฎการ parse YAML ตอนเขียน config

กับดักทั้งสองจับได้เพราะผมคลิกปุ่มจริง ๆ เทสผ่านไม่เท่ากับ feature ทำงาน การยืนยันด้วยมือหลัง refactor ข้ามไม่ได้

Flow เต็มของ refactor กับ Claude

  1. รันเทสทั้ง suite ยืนยันเขียว ถ้าโค้ดเป้าหมายไม่มีการคลุม ให้ Claude เขียนเทสขั้นต่ำที่ล็อกพฤติกรรมปัจจุบันก่อนแล้ว commit
  2. บอกว่า surface ไหนที่อยากดึงออก "Refactor" ใช้ได้เมื่อความซ้ำซ้อนชัดเจน ถ้าไม่ชัด ให้บอก "ดึง map field ของ X ออกไปเป็น class method ใน Y"
  3. ตัดเป็น commit อะตอม หนึ่ง commit หนึ่งเรื่อง
  4. อ่าน diff เอง ลบอะไร map field ยังเทียบกันได้ไหม มีแก้ระหว่างทางไหม
  5. รันเทสอีกครั้ง เขียว
  6. ถ้าแตะ path ที่ผู้ใช้เห็น คลิกผ่าน feature ด้วยมือ ชั้นที่เทสเข้าไม่ถึง — JS, importmap, CDN, parse YAML — ต้องเห็นด้วยตาตัวเอง

Refactor คือสถานการณ์ "ปล่อย Claude ขับ" ที่เสี่ยงที่สุด รั้วไม่ใช่สำหรับ Claude แต่สำหรับคุณ — เพื่อให้เมื่อ Claude ทำพลาด คุณจับได้ใน 5 นาที ไม่ใช่ตอน production ไฟไหม้