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

Можно

  • добавить новое поле с новым номером;
  • добавить новый метод в сервис;
  • добавить новое значение enum с осторожной обработкой неизвестного значения;
  • игнорировать неизвестные поля на чтении.

Старый клиент пропустит новые поля, старый сервер сохранит их при обычном разборе.

Нельзя

  • менять номер существующего поля;
  • менять его тип;
  • переиспользовать номер удалённого поля;
  • переименовывать значение enum.

Удалённые имена и номера резервируют:

reserved 2, 4 to 8;
reserved "legacy_status";

Это защищает от будущего повторного использования.

Незнакомые поля теряются на пути

Сохранение неизвестного поля не гарантирует его доставку. Прокси, преобразование в JSON и обратно или ручная сборка нового сообщения могут молча его выбросить. Поэтому проверяют всю цепочку, а не только клиент и сервер.

buf breaking помогает находить несовместимые изменения до релиза, но не заменяет контрактные тесты со старой и новой версиями.

Две версии в одном rollout

Во время выката старый клиент может говорить с новым сервером, а новый клиент - со старым. Поэтому сначала выпускают сервер, который понимает новые поля, но не требует их. Клиенты начинают слать поля после того, как все серверы обновлены.

старая версия id = 1 status = 2 новая версия id = 1 status = 2 coupon = 3

Новое поле получает новый номер. Старая версия пропускает его при чтении, поэтому номера удаленных полей резервируют.

Для enum заранее описывают поведение неизвестного значения. switch без default - готовый источник неправильной логики: новый статус нельзя молча принять за старый или выполнить действие по умолчанию.

Контрактный тест сериализует сообщение старой версией, читает новой и проверяет обратный путь. Для критичных полей проверяют ещё и сохранение в базу и Kafka: совместимость protobuf сама по себе не спасает от потери данных при преобразовании.

Коротко

  • Добавлять поля и методы безопасно, менять номера и типы нельзя.
  • Удалённые имена и номера резервируют.
  • Неизвестные поля теряются на преобразователях, не только на старых клиентах.
  • Совместимость проверяют автоматически и на двух версиях.