Refactoring a Prototype with an Agent
How to turn a prototype into maintainable code in small safe steps: locking behavior, duplicates, giant components, and N+1 queries.
Lesson content is currently available in Russian.
What you will learn
- →Формулировать рефакторинг-промпты с главным ограничением «не меняй поведение».
- →Вести рефакторинг шагами «одно изменение — один коммит» с git как страховкой.
- →Устранять типовые проблемы vibed-кода: дубли, файлы-гиганты, N+1-запросы.
Что такое рефакторинг и зачем он прототипу
Рефакторинг — термин, канонизированный Мартином Фаулером: изменение внутренней структуры кода без изменения его внешнего поведения. Приложение после рефакторинга делает ровно то же самое — но код становится проще читать, менять и проверять.
Зачем это прототипу, который «и так работает»? Потому что vibed-код оптимизирован под скорость демонстрации, а не под жизнь. Агент копировал блоки вместо переиспользования, складывал запросы, логику и вёрстку в один файл, оставлял мёртвый код от прошлых итераций. Каждая следующая фича в таком коде обходится дороже предыдущей: агент (и вы) всё хуже понимаете, что где лежит, правки в одном месте ломают другое, а внезапные регрессии съедают вечера. Ваш файл TECH_DEBT.md из третьего урока — это, по сути, готовый список причин.
Ключевое правило перед стартом: сначала зафиксируй поведение, потом меняй структуру. Фиксация — это как минимум git-коммит рабочей версии и короткий список ручных проверок («могу войти, могу добавить привычку, календарь показывает отметки»), а в идеале — автотесты, которым посвящён следующий урок. Без фиксации вы не отличите «рефакторинг прошёл чисто» от «рефакторинг тихо сломал оплату».
И обратная сторона: не рефакторьте всё подряд. Код, который работает и который никто не собирается менять, можно не трогать. Приоритет — места, куда вы планируете добавлять фичи, и места из списка красных флагов.
Маленькие шаги и промпт «не меняй поведение»
Главная ошибка — попросить агента «отрефактори весь проект». Получите гигантский дифф на сорок файлов, который невозможно проверить, и лотерею «что из этого сломалось». Дисциплина та же, что при итерациях в уроке 2: один рефакторинг — один промпт — один коммит.
Скелет рабочего рефакторинг-промпта:
Задача: вынеси три копии логики расчёта серии (streak)
из HabitCard.tsx, HabitList.tsx и StatsPage.tsx
в одну функцию calcStreak в lib/streaks.ts.
Ограничения:
- поведение приложения не должно измениться;
- не трогай другие файлы;
- не переименовывай ничего, кроме новой функции;
- в конце перечисли все изменённые файлы и что в них поменялось.
Три элемента делают этот промпт безопасным. Явная цель с именами файлов — агент не гуляет по проекту. Ограничение «поведение не меняется» — это определение рефакторинга, произнесённое вслух; без него агент охотно «улучшит» логику по пути. Требование отчёта об изменённых файлах — ваш инструмент контроля: сверьте список с реальным диффом, и если агент тронул лишнее — откатывайте.
Порядок цикла: коммит рабочей версии → промпт → чтение диффа (git diff или вкладка изменений в инструменте) → ручная проверка списка сценариев → коммит с осмысленным сообщением. Дифф больше сотни строк на шаг — сигнал, что шаг надо было дробить. Git здесь не бюрократия, а страховка: любой неудачный шаг стоит одну команду git revert, а не вечер раскопок.
Типовые рефакторинги vibed-кода: дубли, гиганты, N+1
Три операции покрывают большинство находок из TECH_DEBT.md.
Извлечение дублей. Одинаковая логика в трёх компонентах — выносится в одну функцию или хук. Правило одного источника истины: багфикс должен требовать правки в одном месте.
Разрезание файлов-гигантов. Компонент на 600 строк режется по осям ответственности: данные (запросы к базе) — в отдельный модуль или хук, вычисления — в чистые функции, вёрстка — в мелкие компоненты. Промпт: «разбей StatsPage.tsx: запросы в hooks/useStats.ts, расчёты в lib/stats.ts, вёрстку оставь на месте; поведение не меняй».
Устранение N+1. Самая коварная проблема производительности сгенерированного кода. Выглядит так:
// Антипаттерн: 1 запрос за списком + N запросов в цикле
const { data: habits } = await supabase.from('habits').select('*');
for (const habit of habits) {
const { data } = await supabase
.from('checkins')
.select('*')
.eq('habit_id', habit.id);
}
На пяти тестовых привычках — незаметно. На пятистах записях реального пользователя — сотни сетевых запросов и интерфейс, замерзающий на секунды. Лечение — один запрос вместо цикла:
// Один запрос со связью — Supabase соберёт вложенные данные сам
const { data: habits } = await supabase
.from('habits')
.select('*, checkins(*)');
Как находить: глобальный поиск await внутри циклов for и map, либо промпт «найди в проекте все места, где запрос к базе выполняется в цикле, и предложи замену одним запросом».
Контроль результата: дифф, отчёт, прогон
Рефакторинг агентом безопасен ровно настолько, насколько строг ваш контроль. Три уровня проверки после каждого шага.
Уровень один — дифф. Прочитайте изменения глазами, даже если не понимаете каждую строку. Вы ищете ответы на три вопроса: тронуты ли только заявленные файлы? нет ли в диффе изменений логики (новые условия, изменённые формулы, другие значения по умолчанию) там, где обещана «только структура»? не исчезло ли что-то, что раньше было (обработка ошибок, проверки прав)? Подозрительное место — прямой вопрос агенту: «объясни, почему в диффе изменилось условие в строке 42, если поведение не должно меняться».
Уровень два — отчёт агента против реальности. Вы просили перечислить изменённые файлы — сверьте список с git status. Расхождение в любую сторону — повод откатить шаг: агент, который трогает не то, что декларирует, в этом шаге не заслуживает доверия.
Уровень три — прогон приложения. Запустите прототип и пройдите свой список ручных проверок из первой секции: вход, основной сценарий, критичная фича. Пять минут на прогон после каждого шага дешевле, чем вечер поиска регрессии через десять шагов.
Если все три уровня чисты — коммит и следующий шаг. Если хотя бы один грязный — откат, уточнение промпта, повтор. Этот цикл кажется медленным ровно до первого пойманного на диффе «тихого улучшения логики», которое стоило бы вам данных пользователей.
Practice
Три рефакторинга из TECH_DEBT.md
Выберите из своего TECH_DEBT.md три пункта: один дубль, один файл-гигант и один N+1 (или запрос в цикле). Проведите каждый через полный цикл: коммит → промпт с ограничением «не меняй поведение» → дифф → прогон → коммит.
Deliverable
Три коммита в репозитории с осмысленными сообщениями + краткая заметка по каждому: промпт, размер диффа, что показал прогон.
Checkpoint
Агент вернул дифф на 40 файлов в ответ на просьбу вынести одну функцию. Ваши действия и почему?
- ✓ Решение: откат/отклонение диффа, а не попытка проверить 40 файлов глазами.
- ✓ Причина: шаг слишком крупный для проверки, отчёт расходится с задачей — доверять изменению нельзя.
- ✓ Следующий шаг: уточнить промпт ограничениями (какие файлы можно трогать) и повторить меньшим шагом.
Check yourself
Sources