← Vibe Coding — and How to Take It to Production
05 / 07 · 50 min

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

Что такое рефакторинг по определению Фаулера?

01Что такое рефакторинг по определению Фаулера?

Что нужно сделать ДО начала рефакторинга?

02Что нужно сделать ДО начала рефакторинга?

Почему запрос к базе внутри цикла (N+1) опасен именно для vibed-приложений?

03Почему запрос к базе внутри цикла (N+1) опасен именно для vibed-приложений?

Агент отчитался, что изменил два файла, а git status показывает пять. Что это значит?

04Агент отчитался, что изменил два файла, а git status показывает пять. Что это значит?

Sources

Refactoring a Prototype with an Agent · Vibe Coding — and How to Take It to Production · VORCL