Коротко: в большинстве случаев правильно рефакторить, а не переписывать: big bang rewrite чаще проваливается; золотая середина — паттерн Strangler Fig.
В подавляющем большинстве случаев правильный ответ — рефакторить, а не переписывать с нуля. Полная переписка («big bang rewrite») заманчива, но статистически чаще проваливается: вы годами догоняете функциональность, которую старая система уже умеет, и попутно теряете знания, зашитые в её «костылях». Разберу, когда это правило всё-таки можно нарушить.
Почему переписать «с нуля» так заманчиво — и так опасно
Старый код всегда выглядит хуже, чем он есть: вы видите его недостатки, но не видите сотни багфиксов и краевых случаев, которые в нём уже учтены. Джоэл Спольски не зря называл полную переписку «худшей стратегической ошибкой». Главные риски big bang rewrite:
- Потеря неявных знаний. Каждый странный
if— это, как правило, чей-то реальный инцидент в прошлом. - Заморозка развития. Пока вы переписываете, бизнес не получает новых фич, а конкуренты не ждут.
- «Догоняющая» разработка. Новый код должен сначала просто сравняться со старым по возможностям — это месяцы без видимой пользы.
Когда рефакторинг — правильный выбор (почти всегда)
Если система работает и приносит деньги, а проблема в том, что в неё тяжело вносить изменения, — это случай для постепенного улучшения. Я иду так: накрываю критичные пути характеризационными тестами, выделяю модули, чищу по «правилу бойскаута» (оставь код чуть лучше, чем нашёл) и улучшаю то, к чему всё равно прикасаюсь по задачам. Бизнес при этом продолжает получать фичи.
Когда переписывание оправдано
Полная или частичная переписка имеет смысл, если честно выполняется несколько условий сразу:
- Технология мертва. Платформа/язык/фреймворк сняты с поддержки, нет обновлений безопасности, некого нанять.
- Изменения физически невозможны. Любая правка ломает три других места, тестов нет и написать их нельзя.
- Бизнес-модель изменилась. Система проектировалась под другую задачу, и натягивание новой на старую архитектуру дороже переписки.
- Есть ресурс пережить «догоняющий» период. Если бизнес не выдержит месяцы без новых фич — переписка убьёт продукт.
Золотая середина — Strangler Fig
Чаще всего правильный путь — не «или-или», а постепенное замещение. Паттерн «душащего дерева» (Strangler Fig, описан Мартином Фаулером): вокруг старой системы выращивается новая, по кускам перехватывая функциональность, пока старое не отомрёт. Никакого «дня X», когда всё переключается разом.
// фасад/прокси решает, куда направить запрос:
// уже переписанный модуль — в новый сервис, остальное — в легаси
public function handle(Request $request): Response
{
if ($this->migrated->supports($request->getPathInfo())) {
return $this->newService->handle($request); // новая реализация
}
return $this->legacyService->handle($request); // пока старая
}
Так риск дробится на маленькие обратимые шаги, а пользователь не замечает «переезда».
Как я принимаю решение на практике
- Что именно болит — поддержка, производительность, безопасность, наём? Сформулировать проблему, а не «всё плохо».
- Можно ли решить это рефакторингом за обозримый срок? Обычно да.
- Если нет — можно ли заместить по кускам (Strangler Fig)? Обычно да.
- И только если оба «нет» — обсуждать полную переписку, честно заложив «догоняющий» период и риски.
Итог
«Перепишем с нуля» — самый дорогой и самый соблазнительный ответ. В реальности побеждает скучная стратегия: тесты на легаси, постепенный рефакторинг и замещение по Strangler Fig. Переписка с чистого листа оправдана редко и только при честно выполненных условиях.
Если стоите перед этим выбором и не хотите ошибиться ценой полугода работы команды — разберём вашу ситуацию на консультации.


Комментарии