Предыстория
Zend Framework официально прекратил поддержку в конце 2019 года и перешёл в Laminas Project. Для большинства компонентов это была простая замена неймспейса: Zend\ → Laminas\. Но в нашем случае биллинговая система работала на продакшне круглосуточно, а код содержал несколько самописных расширений под конкретные версии Zend-компонентов.
Перейти «одним коммитом» было нельзя — слишком большой риск.
Стратегия
Разбил миграцию на три фазы, каждую из которых можно откатить независимо.
Фаза 1: Зависимости
Обновил composer.json: заменил zendframework/* пакеты на laminas/* эквиваленты. Laminas предоставляет пакет совместимости laminas/laminas-zendframework-bridge, который на уровне автолоадера алиасит старые неймспейсы на новые. Это позволило запустить систему без изменения ни одной строки PHP-кода.
Развернул окружение в Docker, прогнал функциональные тесты — всё прошло. Выкатил на стейдж.
Фаза 2: Замена неймспейсов
Написал скрипт на базе rector/rector, который автоматически переименовал Zend\ → Laminas\ по всей кодовой базе (~18 000 строк). Прогнал тесты после каждого модуля, а не после всего сразу.
Трёх мест автоматическая замена не решила: там были строковые имена классов в конфигах и один рефлексивный вызов. Правил руками.
Фаза 3: Удаление моста совместимости
После того как неймспейсы заменены и всё работает, убрал laminas-zendframework-bridge из зависимостей. Это финальная точка — теперь код зависит только от Laminas.
Деплой без даунтайма
Биллинг не мог останавливаться. Использовали стандартную схему: сначала выкатили фазу 1 (только зависимости) с мостом совместимости — ничего не поменялось для пользователей. Потом поэтапно фазы 2 и 3.
В каждой точке был зафиксирован снепшот базы данных и образ Docker — для быстрого отката. Фактически откатываться не пришлось ни разу.
Что узнал
Ключевой момент — не пытаться сделать всё за один раз. Мост совместимости даёт время, чтобы прогнать тесты и убедиться, что система работает на новой основе, не меняя при этом продуктовый код.
Rector значительно ускоряет механическую работу по переименованию, но не заменяет понимание архитектуры — нужно знать, где строки с именами классов живут в конфигах.
Итог
Миграция заняла около трёх недель в фоновом режиме. Система получила актуальную кодовую базу, поддерживаемые зависимости и возможность обновляться дальше. Без единой минуты даунтайма.