Legacy и миграции

·5 мин чтения·Обновлено

Branch by Abstraction: заменяйте реализацию, не замораживая продукт

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

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

Branch by Abstraction предлагает другой порядок: сначала отделить вызовы от конкретной реализации, затем менять её небольшими шагами в общей линии разработки. Пока замена не закончена, приложение остаётся пригодным для обычных выпусков. Цена такого подхода — временное сосуществование вариантов и работа по их проверке, а не отсутствие миграционной сложности.

Сначала провести границу вокруг того, что уже работает

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

Эта последовательность описана в Branch by Abstraction у Мартина Фаулера. Новая реализация появляется за той же границей, а потребителей переключают в подходящем порядке. Часто это можно делать по частям; иногда условия приложения требуют общего переключения.

Найти все вызовы бывает сложнее, чем написать интерфейс. Ночная выгрузка может напрямую создавать объект старой библиотеки, а расширение поддержки — использовать её внутренний формат. Такие обходы нужно либо перевести, либо явно учесть. Иначе библиотека останется необходимой после того, как основной экран уже работает иначе.

Не копировать старую библиотеку в новый контракт

Интерфейсу не обязательно повторять десятки её методов. Но и слишком красивое название «сделать документ» мало помогает без договорённости о результате. Какие данные обязательны? Как передаются ошибки? Кто хранит файл? Может ли операция выполняться повторно? Где проверяется право получить акт?

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

AWS сравнивает этот приём со Strangler Fig: внутреннюю зависимость не всегда можно перехватить на внешнем входе приложения. Branch by Abstraction работает с такой границей в коде. Отдельный сетевой сервис для новой реализации необязателен.

Сравнить документы, не отправляя клиенту два письма

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

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

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

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

Новый код выключен, но уже участвует в выпуске

Обе реализации собираются и проверяются вместе с продуктом. Именно возможность регулярно выпускать общую линию подчёркивает руководство по Trunk-Based Development. Незаконченная замена не должна превращать каждое исправление для клиента в ожидание миграционного проекта.

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

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

Выбрать единицу переключения и предел возврата

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

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

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

Что остаётся после замены

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

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