Рефакторинг не має симптомів — помилок Claude не видно. Три огорожі: тести, атомарні коміти, ручний клік.
Рефакторинг — задача, у якій Claude найнебезпечніший. Баг має симптоми — кнопка не реагує, значення undefined, стек-трейс — тому ти можеш сказати, чи спрацював фікс Claude. У рефакторингу симптомів немає. «Все ще працює» може означати «тести все ще проходять», а поведінка тихо змінилася, ти не помітив і через тиждень продакшен палає.
Нещодавно я зробив з Claude доволі великий рефакторинг у how2claude: переніс крипто-платежі x402 з власноруч написаних PaymentHandler + FacilitatorClient (139 рядків) на gem x402-rails і водночас виніс мапінг полів Purchase.create! / Subscription.create! — продубльований у двох контролерах — у класові методи моделі. Один коміт, 4 файли змінено, 2 видалено, 2 додано.
Мій промпт був одним словом: «refactor».
Таким коротким він міг бути тому, що навколо стояли огорожі.
До моменту, коли гілка дійшла до цього місця, у ній було 221 тест. Усі критичні шляхи потоку оплати покриті.
Стандартна дія Claude перед рефакторингом — не «спершу подивитися тести». Тому я сказав йому спершу запустити bin/rails test, підтвердити зелене, і лише потім щось чіпати.
Після рефакторингу прогнати ще раз. Все ще зелене. Це не означає «регресій немає» — це означає, що відома поведінка не зламана.
Якщо твій кодпоч не вкритий тестами, нехай Claude напише мінімальний тест, що фіксує поточну поведінку. Прогони, закомміть. Аж потім рефакторинг. Інакше те, що він робить, — не рефакторинг, а переписування, і ти не маєш способу перевірити еквівалентність.
Цей рефакторинг насправді був двома речами:
Purchase / Subscription: контролер → класові методи моделіУ фронтенді була третя: переписати підпис на JS-боці через viem + x402-fetch.
Я примусив Claude поділити за природними межами: бекенд + витяг моделі — один коміт (9f3e239), фронтенд — окремий (93746d8). Кожен коміт несе повний опис, список файлів і причину зміни.
Переваги:
- Дифи залишаються читабельними. Один коміт, одна річ.
- Гранулярність відкату. Якщо в проді знайшли баг фронтенду, git revert 93746d8 відкочує лише фронт, бекенд залишається.
- Увага самого Claude залишається сфокусованою. Один коміт, одна річ — і його увага накриває лише цю річ.
Коли рефакторинг завершений, я зупиняю Claude і прошу показати git diff --staged. Тести не запускати, додаток не запускати — спершу читати диф.
Сигнали, які я сканую:
app/services/x402/payment_handler.rb видалений цілком — ок, це сенс міграції на gem. Але якщо видалив те, що я не просив чіпати, я зупиняюся і запитую.Purchase.create!(wallet_address: verify_result["payer"], ...) → Purchase.record_x402!(payment:, settlement:) тепер читає payment[:payer]. Джерело змінилося (request.env у gem vs значення, яке повертав старий клієнт), але поля мають відображатися один до одного.Пастка 1: Stimulus-контролери gem мовчки не завантажилися
gem x402-rails постачається з власними Stimulus-контролерами. Claude написав код, тести пройшли зеленими. Я вручну клацнув кнопку оплати — нічого.
Причина: у config/importmap.rb pin для @hotwired/stimulus указував на неіснуючий vendor-файл, і importmap мовчки викинув цей pin. Контролери gem так і не завантажилися. Тести цього не ловлять, бо bin/rails test не виконує JS.
Пастка 2: YAML розпарсив 0x... як integer
wallet_address: 0x833589... — без лапок. YAML побачив префікс 0x, прочитав як шістнадцятковий integer. Facilitator отримав не-рядок і відхилив. Claude, пишучи конфіг, не зупинився подумати про правила парсингу YAML.
Обидві пастки спіймалися тому, що я клацнув реальну кнопку. Зелені тести — не те саме, що робоча фіча. Ручна перевірка після рефакторингу не опціональна.
Рефакторинг — сценарій «пустити Claude за кермо» з найвищим ризиком. Огорожі не для Claude. Вони для тебе — щоб, коли Claude помилиться, ти зловив це за 5 хвилин, а не під час пожежі в проді.