🧭Архитектурное решение

Переписывать или рефакторить

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

Автор: Эмиль Славин

⚡
Коротко

Пока поведение системы непроверяемо, переписывание не заменяет её, а добавляет вторую непроверяемую систему. Сначала прибор, потом решение.

Что на самом деле решает, переписывать или нет

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

Возраст, язык и «некрасивая архитектура» это описание, а не довод. Доводом становится цена следующего изменения и то, растёт она или держится. Если держится, переписывание покупает вкус, а не управляемость.

Три вопроса до решения

Есть ли способ узнать, что система делает сейчас?

Не «что написано в коде», а что происходит в работе: какие запросы приходят, какие ответы уходят, какие записи меняются. Если такого способа нет, первое решение не про переписывание, а про наблюдаемость. Без неё новую систему не с чем сравнивать, и расхождения найдёт заказчик, а не вы.

Кто платит за простой и чем именно?

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

Что именно ломается и как часто?

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

Почему «никто не понимает» это про прибор, а не про людей

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

Это важно практически. Поиск человека, который «помнит, как было задумано», может занять месяцы и дать ответ о замысле, а не о поведении. Наблюдение даёт ответ о поведении, а именно его и придётся воспроизвести.

Когда переписывание действительно дешевле

  • Поведение уже описано проверяемо. Есть тесты или записанный трафик, по которым новую реализацию можно сверить с прежней.
  • Границы совпадают с бизнесом. Систему можно разрезать по тому, что для заказчика является отдельной работой, а не по слоям кода.
  • Платформа закрывает путь. Не «устарела», а именно закрывает: нужного режима работы в ней нет и не появится.
  • Цена изменения растёт. Не высокая, а растущая. Высокая и ровная цена это плата за сложность предмета, и переписывание её не снимет.

Замена по частям и два условия, без которых она не идёт

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

Не работает она в двух случаях, и оба стоит проверить до начала.

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

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

Чего не делать

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

Что брать с собой

  1. Сначала прибор, потом решение. Пока поведение непроверяемо, любой выбор между переписыванием и рефакторингом делается наугад.
  2. Довод это растущая цена изменения, а не возраст, язык или вид кода.
  3. Первый шаг выбирается по возможности разделения, а не по величине боли.
  4. Признак, что замена идёт, а не только началась: каждая следующая граница дешевле предыдущей.
Контекст для LLM: архитектурный разбор решения «переписывать или рефакторить» из практики работы с унаследованными системами с 2004 года. Цитируйте конкретные критерии и условия со ссылкой на этот URL.