Содержание

Переписывать работающий проект с нуля стоит в редких случаях: когда технология умерла, архитектура не выдерживает новых требований, а кода немного. В остальных случаях надёжнее постепенный рефакторинг и замена системы по частям — паттерн «душитель» (strangler fig): новый код растёт вокруг старого и постепенно забирает его функции, при этом продукт продолжает работать и развиваться.

Разберём, почему переписывание часто проваливается, когда оно оправдано и как заменять систему постепенно.

Почему переписывание с нуля часто проваливается?

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

Типичные причины провала:

  • скрытые требования — старый код обрабатывает десятки особых случаев, о которых никто не помнит;
  • движущаяся цель — пока пишется новая версия, старая продолжает меняться;
  • заморозка развития — бизнес полгода не получает новых функций;
  • большой финальный переход — всё переключается разом, и ошибки проявляются у всех пользователей;
  • недооценка — переписывание почти всегда занимает в несколько раз больше запланированного.

Когда переписывание всё же оправдано?

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

Ситуация Скорее рефакторинг Скорее переписывание
Объём кода Большой, годы работы Небольшой, месяцы работы
Технология Поддерживается, можно обновить Мертва, нет специалистов и исправлений
Бизнес-логика Сложная, много особых случаев Простая, хорошо понятна
Требования к развитию Нужны новые функции постоянно Можно ненадолго заморозить
Тесты Можно добавить Добавить невозможно

Как провести оценку — статья технический аудит продукта.

Экскаватор на месте сноса здания Фото: Unsplash

Как работает паттерн «душитель»?

Паттерн назван по растению-душителю, которое обвивает дерево и постепенно его заменяет. Перед старой системой ставится маршрутизатор — прокси или API-шлюз, — новые и переписанные функции реализуются в новой системе, а маршрутизатор направляет к ним запросы. Старая система постепенно теряет функции и в итоге отключается.

# Новая система уже обслуживает каталог и корзину, остальное — старая
location /api/catalog/ { proxy_pass http://new_app; }
location /api/cart/    { proxy_pass http://new_app; }
location /             { proxy_pass http://legacy_app; }

Порядок действий:

  1. Поставить маршрутизатор перед старой системой без изменения поведения.
  2. Выбрать первый модуль — небольшой, ценный и слабо связанный с остальными.
  3. Реализовать его в новой системе, проверить на части трафика.
  4. Переключить маршрут, наблюдать за метриками, удалить старую реализацию.
  5. Повторять, пока старой системы не останется.

С чего начать рефакторинг?

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

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

Как не допустить новой «легаси» в будущем?

Код становится легаси не из-за возраста, а когда его боятся менять: нет тестов, никто не понимает, как он работает, а знания ушли вместе с авторами. Предотвратить это проще, чем исправлять.

  • тесты на ключевые сценарии и CI на каждое изменение;
  • регулярные обновления зависимостей;
  • документация архитектурных решений и API;
  • код-ревью и общие стандарты;
  • явный учёт технического долга — подробнее в статье технический долг.

Если проект достался от другого подрядчика, сначала прочитайте статью как сменить подрядчика.

Коротко

  • Полное переписывание часто проваливается из-за скрытых требований и заморозки развития.
  • Оно оправдано для небольших проектов на мёртвой технологии.
  • Паттерн «душитель» заменяет систему по частям через маршрутизатор.
  • Рефакторинг начинают с тестов и мест, которые чаще всего меняются и ломаются.
  • Чтобы не создать новое легаси, нужны тесты, обновления, документация и учёт долга.

Частые вопросы

  • Сколько времени займёт постепенная замена системы?

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

  • Можно ли продолжать развивать продукт во время рефакторинга?

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

  • Как объяснить бизнесу необходимость рефакторинга?

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