Совместимость API определяется не только сохранением того же маршрута или версии. Потребители также зависят от структуры данных, правил валидации, значений по умолчанию и поведения при ошибках. Если разные клиенты обновляются с разной скоростью, изменение, которое кажется небольшим разработчику сервера, может нарушить работу мобильного приложения, сторонней интеграции или внутреннего процесса.
Политика совместимости данных в PHP API позволяет превратить этот риск в конкретные решения: какие изменения допустимы, как их внедрять и какие проверки должны быть пройдены перед выводом контракта из эксплуатации. Универсального правила, которое гарантировало бы совместимость любых изменений, нет: необходимо учитывать, как каждый потребитель читает и отправляет данные.
Контракт включает данные, валидацию и поведение

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

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



