Коротко: в большинстве случаев правильно рефакторить, а не переписывать: 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);      // пока старая
}

Так риск дробится на маленькие обратимые шаги, а пользователь не замечает «переезда».

Как я принимаю решение на практике

  1. Что именно болит — поддержка, производительность, безопасность, наём? Сформулировать проблему, а не «всё плохо».
  2. Можно ли решить это рефакторингом за обозримый срок? Обычно да.
  3. Если нет — можно ли заместить по кускам (Strangler Fig)? Обычно да.
  4. И только если оба «нет» — обсуждать полную переписку, честно заложив «догоняющий» период и риски.

Итог

«Перепишем с нуля» — самый дорогой и самый соблазнительный ответ. В реальности побеждает скучная стратегия: тесты на легаси, постепенный рефакторинг и замещение по Strangler Fig. Переписка с чистого листа оправдана редко и только при честно выполненных условиях.

Если стоите перед этим выбором и не хотите ошибиться ценой полугода работы команды — разберём вашу ситуацию на консультации.