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

Наличие старого и нового приложения само по себе не означает, что миграция определена. Если оба принимают одну и ту же операцию, они могут применять разные правила проверки, контроля доступа, ценообразования, статусов или побочных эффектов. Обычно проблема обнаруживается не сразу: счёт рассчитывается двумя способами, уведомление отправляется дважды или изменение статуса отражается не во всех системах.
Для постепенной замены legacy-системы на PHP нужно явно описать, какая функция переносится, кто получает запросы и где сохраняется полномочие управлять данными. Граница должна быть функциональной, а не просто представлять собой список контроллеров или таблиц. Например, функция «обработать возврат» может включать авторизацию, обновление запасов и возврат денежных средств; разделение её без понимания этих зависимостей создаёт ложную границу.
Прежде чем менять маршрутизацию, задокументируйте наблюдаемое поведение: входные данные, выходные данные, значимые ошибки, права доступа, побочные эффекты и внешние зависимости. Отличайте бизнес-правила от случайных особенностей реализации. Не всё исторически сложившееся поведение нужно сохранять, но любое намеренное изменение следует согласовать и протестировать как изменение, а не молча списывать на миграцию.
Выберите первую функцию, результат которой можно проверить
Первый кандидат не обязательно должен быть самым маленьким или самым заметным. Лучше отдать приоритет функции с понятными границами, умеренной связанностью и проверяемым результатом. Важна и операционная ценность: миграция может снизить риск, упростить частые изменения или улучшить часть системы, которую бизнесу необходимо развивать.
- Связанность: выявите вызовы других модулей, прямой доступ к общим таблицам, запланированные задачи и зависимости от глобального состояния. Многочисленные связи не исключают миграцию, но увеличивают стоимость определения границ.
- Проверяемость: выясните, можно ли сформулировать репрезентативные сценарии и наблюдать результат без субъективных интерпретаций.
- Влияние и обратимость: оцените последствия сбоя функции и возможность вернуться к прежнему маршруту без потери данных и повторения внешних эффектов.
- Операционная готовность: убедитесь, что до направления реального трафика есть ответственные, метрики и процедуры расследования расхождений.
Поможет краткий перечень: функция, её потребители, данные, которые она читает и изменяет, внешние эффекты, владелец со стороны продукта и технический владелец. Если неизвестно, кто использует операцию или какая система обновляет данные, сначала нужно лучше разобраться в системе: немедленная миграция неопределённость не уменьшит.
Определите маршрутизацию и полномочия в отношении данных
Маршрутизатор должен явно решать, какая реализация обрабатывает запрос. В зависимости от архитектуры это может быть слой приложения, gateway или логика в точке входа. Важно, чтобы критерий был наблюдаемым и последовательным: по функции, клиенту, операции или контролируемой когорте. Не допускайте, чтобы два маршрута обрабатывали один заказ без координации.
На каждой границе назначьте владельца для каждого набора данных или агрегата. Если, например, владельцем заказов стала новая система, старая не должна продолжать напрямую изменять их через общий доступ к таблицам. Другие компоненты могут обращаться к определённому интерфейсу или получать изменения через события — в зависимости от требований к согласованности. Общая база данных может быть временным этапом, но это не означает, что полномочия по управлению данными тоже общие.
В синхронизации необходимо явно указать направление и назначение. Копия для чтения может использоваться для отчётов или старых потребителей, но не должна случайно превращаться во второй авторитетный источник для записи. При обмене событиями определите идентификаторы, порядок обработки, если он необходим, повторные попытки, дедупликацию и обработку неудачных сообщений. Также уточните, как обнаруживаются задержки и кто устраняет расхождения.
Избегайте неоднозначной двойной записи
Запись в две системы в рамках одного запроса кажется простым способом поддерживать их согласованность, но приводит к частичным сбоям: одна запись может завершиться, а другая — нет. Повторный запрос способен продублировать операцию. Без стратегии идемпотентности, согласования данных и обработки ошибок не существует единой транзакции, которая гарантировала бы согласованность независимых приложений.
Для каждой операции отдавайте предпочтение одному источнику, уполномоченному выполнять запись. Во время перехода система-владелец может публиковать изменения для обновления проекции или копии, необходимой для совместимости. Для асинхронных процессов надёжно регистрируйте изменение и связанное с ним сообщение в рамках одной транзакционной границы, если это позволяет инфраструктура, и проектируйте идемпотентных потребителей. Если конкретное ограничение требует двойной записи, рассматривайте её как временное исключение: укажите порядок, возможные сбои, способ согласования и дату либо условие её устранения.
Для исторической копии данных также нужны правила. Определите, как загружается исходное состояние, как применяются параллельные изменения и как проверяется результат до смены владельца. Не исходите из того, что успешный импорт гарантирует полноту связей, производных статусов и прав доступа.
Сравнивайте поведение, не дублируя эффекты
Сравнение снижает риск, если отделять вычисление от выполнения. Например, можно передать копию входных данных новой реализации в режиме наблюдения и сравнить её ответ с ответом активной системы, не позволяя копии отправлять письма, принимать оплату, менять запасы или вызывать другие внешние эффекты. Для такого выполнения нужно использовать контролируемые данные и зависимости либо заменять внешние эффекты адаптерами, которые только регистрируют намерение.
Сравнивайте семантические результаты: статусы, суммы, решения и значимые ошибки, а не обязательно порядок полей или временные метки. Используйте репрезентативные сценарии, включая граничные случаи, неполные данные, права доступа и сбои зависимостей. Добавьте автоматизированные тесты для критически важных правил и сохраняйте анонимизированные или синтетические примеры, если реальные данные не подходят для проверки.
Выполнение в теневом режиме само по себе не доказывает, что новая система готова к работе. Оно может не воспроизводить задержки, конкурентное выполнение или реальные интеграции, а расхождение может указывать как на дефект, так и на недокументированное старое правило. Для каждого расхождения нужны классификация, ответственный и решение: исправить, принять согласованное изменение или исключить случай с проверяемым обоснованием.
Инструментируйте переход и подготовьте откат
Регистрируйте выбранный маршрут, версию реализации, идентификатор корреляции и результат каждой операции. Измеряйте ошибки, задержки, повторные попытки, задержки синхронизации и расхождения в разрезе функций. Не записывайте секреты и ненужные персональные данные; определите контроль доступа и сроки хранения журналов.
До увеличения доли трафика согласуйте пороговые условия остановки и определите, кто может остановить переход. Откат не всегда сводится к смене маршрутизации: если новая система уже приняла записи, возврат к старому приложению может оставить его без самого актуального состояния. В процедуре должно быть указано, что делать с выполняющимися операциями, ожидающими сообщениями и записанными данными; отработайте её в подходящей среде. Обратимость зависит от состояния системы и побочных эффектов, а не только от параметра конфигурации.
Выводите старую систему из эксплуатации по проверяемым критериям

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



