Рефакторинг меняет устройство кода, сохраняя его согласованное поведение. Это отличает его от добавления функции или изменения бизнес-правил. Когда задача звучит как «приведи проект в порядок», Codex приходится самостоятельно решать, что считать порядком. В результате небольшое улучшение может превратиться в масштабную переработку.

Для существующего приложения безопаснее начать с одной конкретной проблемы: повторяющаяся валидация, длинная функция, смешение интерфейса и работы с данными. Чем точнее причина, тем проще проверить, что изменение действительно помогло и ничего не сломало.

Назовите проблему, которую решает рефакторинг

«Слишком много строк» само по себе не является дефектом. Полезнее объяснить последствия: одно правило приходится менять в трех местах, тест нельзя написать без запуска базы, обработка ошибки отличается между одинаковыми формами. Тогда можно выбрать минимальное структурное изменение под реальную трудность.

Не соединяйте в одной задаче перенос файлов, смену библиотеки, обновление дизайна и исправление логики. Если результат сломается, будет трудно установить причину. Сначала отделите изменение структуры от изменения поведения. Исключения возможны, но должны быть осознанными и отдельно проверенными.

Зафиксируйте внешний контракт

Внешним контрактом может быть формат API, возвращаемое значение функции, порядок событий, текст ошибки, структура сохраняемых данных или публичный импорт. Даже если новый вариант кажется удобнее, его изменение уже не является чистым рефакторингом для зависимых частей системы.

  • Какие вызовы используют изменяемый модуль?
  • Какие значения и ошибки они ожидают?
  • Есть ли зависимости на порядок действий или побочные эффекты?
  • Какие файлы и форматы должны остаться совместимыми?

Для старого кода полезны characterization-тесты: они фиксируют текущее наблюдаемое поведение. Но не следует автоматически превращать известный баг в вечное требование. Спорные случаи сначала обсуждают и отмечают отдельно.

Пример ограниченной задачи

Цель: убрать дублирование проверки телефона
в двух существующих обработчиках заявок.
Сначала найди оба вызова и текущие тесты.
Сохрани допустимые форматы и тексты ошибок.
Выдели общий helper в принятом для проекта месте.
Не меняй публичные API и зависимости.
Не форматируй несвязанные файлы.
Добавь тесты сохранения текущего поведения.
В отчете покажи, что стало общим и что не изменилось.

Если обнаружилось, что обработчики намеренно принимают разные форматы, общий helper может оказаться неправильным решением. В таком случае агент должен объяснить различие, а не добиваться объединения любой ценой. Удаление похожего кода полезно только при совпадающей ответственности.

Работайте небольшими переходами

Сначала добавьте защитные тесты. Затем выделите функцию или модуль без изменения вызовов. После этого переключите один путь, проверьте его и только потом переходите к следующему. Такой порядок облегчает диагностику. Не обязательно делать отдельный коммит на каждую строку, но этапы должны оставаться понятными при просмотре diff.

Если в рабочем каталоге уже есть изменения пользователя, их нельзя считать мусором или откатывать для удобства. Зафиксируйте исходное состояние, согласуйте область работы и сохраняйте чужие правки. Для крупного эксперимента используйте отдельную рабочую копию, чтобы не смешивать результаты.

Проверяйте не только тесты, но и совместимость

После перемещения функции могут сломаться импорты, сборка пакета или вызовы из другой части репозитория. Проверьте публичные точки входа и поиск ссылок на старое имя. Для сериализуемых данных важно убедиться, что старые записи по-прежнему читаются.

Производительность тоже может измениться: общий helper иногда добавляет лишние запросы или повторные вычисления. Если участок чувствителен к нагрузке, измеряйте сопоставимый сценарий до и после. Не объявляйте код более быстрым только потому, что он стал короче.

Когда нужно остановиться

Повод остановить текущий этап: пришлось менять публичный контракт, появилась необходимость миграции данных, затронуты несколько независимых модулей или тесты не позволяют отличить старое поведение от нового. Это не провал. Значит, задача оказалась шире исходной и требует уточнения.

Можно попросить Codex сначала перечислить риски и предложить минимальный план без правок. Такой способ соответствует работе с контекстом и ограничениями из руководства по постановке задач. При этом решение о допустимом изменении поведения остается за владельцем проекта.

Как принимать результат

Посмотрите, соответствует ли diff исходной цели. Обновление зависимостей и массовое форматирование не должны прятаться среди полезных изменений. Попросите объяснить новый путь выполнения на одном примере: где теперь проверяется вход, где формируется результат и где обрабатывается ошибка.

Отчет должен содержать выполненные проверки и ограничения. Если общие тесты уже падали до начала работы, это нужно отметить, а не выдавать за новую ошибку или молча исправлять соседнюю систему. Для новой структуры желательно обновить документацию там, где она действительно изменилась.

Что дает хороший рефакторинг

Результат измеряется удобством следующей конкретной правки: правило находится в одном месте, модуль можно проверить независимо, ответственность понятна. Не каждое изменение уменьшает число строк, и это нормально. Дополнительный явный код иногда снижает связанность лучше сложной универсальной абстракции.

Перед стартом пригодятся границы архитектуры и тесты на поведение. Если старый сайт сложно поддерживать, начнем с технического разбора: возможно, достаточно точечной доработки вместо полного переписывания.