📬 Пост 48. Версионирование в gRPC - как не сломать клиентов
Если работали с REST, привычно делать: /api/v1/orders /api/v2/orders В gRPC подход немного другой. Здесь большую роль играет Protocol Buffers, который изначально рассчитан на эволюцию контрактов. Главное правило: 👉 не создавать v2 при каждом изменении. Сначала пытаемся сделать изменение обратно совместимым.
🧠 Почему protobuf позволяет менять контракт? У каждого поля есть уникальный field number: message User { int64 id = 1; string name = 2; } Можно безопасно добавить: string email = 3; Старый клиент просто не знает про поле 3 и проигнорирует его. А вот такое изменение опасно: string name = 2; на: int64 name = 2; Мы поменяли тип существующего поля - старый клиент может сломаться.
🚨 Что нельзя делать с protobuf ❌ менять field number ❌ менять тип существующего поля ❌ переиспользовать номер удалённого поля ❌ бездумно удалять поля и RPC Если поле удаляем, его номер лучше зарезервировать: reserved 2; reserved "name"; Чтобы кто-нибудь через полгода случайно не использовал 2 для другого поля.
⚠️ А что делать с устаревшим полем? Сначала помечаем его deprecated: string name = 2 [deprecated = true]; Клиенты постепенно переходят на новое поле. И только потом старое можно удалить и зарезервировать его номер. Получается: deprecated ↓ миграция клиентов ↓ удаление ↓ reserved
🔢 А где вообще хранится версия? Обычно версию указывают в package: package company.order.v1; Если произошёл breaking change и сохранить совместимость уже нельзя: package company.order.v2; Тогда некоторое время могут одновременно существовать: v1 ← старые клиенты v2 ← новые клиенты После миграции клиентов v1 можно вывести из эксплуатации.
💡 Когда нужен v2? Не когда добавили новое поле. А когда изменение нельзя сделать обратно совместимым. Например: было: amount: string стало: amount: object Старый клиент уже не сможет нормально работать с новым форматом. Вот тогда: 👉 v2
⚙️ И ещё важный момент .proto - это контракт. Из него генерируется код клиента и сервера: .proto ↓ protoc ↓ generated code ↓ client / server Поэтому после изменения контракта нужно регенерировать код и проверить совместимость. В нормальном проекте такие проверки ещё запускают в CI, чтобы случайный breaking change не уехал в прод.
🏁 Главная мысль Хорошее версионирование gRPC - это не: изменение → v2 А: изменение ↓ можно сохранить совместимость? ↓ да → меняем текущий контракт нет → создаём v2 Именно поэтому системному аналитику важно понимать не только, как назвать новую версию API, но и какие изменения реально ломают существующих потребителей - всегда это подчеркиваю своим ребятам