Прямой ответ
Protocol Buffers (Protobuf) — язык описания схем данных и сериализации (Google — Protobuf — «схема → код на любом языке» — gRPC — «один .proto — клиенты на Python/Go/Java» — контракты); зачем Protobuf: типизация (схема определяет типы — «string name = 1» — контракты — «схема — контракт» — API-версии), компактность (бинарный формат — меньше JSON — ёмкость — «Protobuf меньше JSON» — gRPC), кросс-языковость (один .proto — код на любом языке — gRPC — «схема → Python/Go/Java» — контракты), эволюция (обратная совместимость — новые поля не ломают старых клиентов — API-версии — «добавил поле — старые клиенты работают» — Protobuf); схема .proto (основа): message (сообщение — структура данных — Protobuf — «message User { string name = 1; int32 age = 2; }» — контракты), поля (тип + имя + номер — «string name = 1» — номер — идентификатор поля в бинарном формате — Protobuf — «номер 1 — не имя» — API-версии), типы (int32/int64/string/bool/bytes/message/enum/repeated — Protobuf — «repeated string — массив» — контракты); эволюция схемы (главное): обратная совместимость (новые поля — старые клиенты игнорируют — Protobuf — «добавил поле — старые клиенты не ломаются» — API-версии), прямая совместимость (старые поля — новые клиенты игнорируют — Protobuf — «удалил поле — новые клиенты не ломаются» — API-версии); правила совместимости (критично): (1) не менять номера полей (номер 1 → номер 2 — breaking — Protobuf — «сменил номер — старые данные не парсятся» — API-версии); (2) не менять типы полей (string → int32 — breaking — Protobuf — «сменил тип — не парсится» — API-версии); (3) добавлять поля — ок (новые поля — старые клиенты игнорируют — Protobuf — «добавил поле — ок» — API-версии); (4) удалять поля — ок, но reserved (удалить поле — зарезервировать номер — «reserved 3» — Protobuf — «удалил поле 3 — зарезервировал — не переиспользовать» — API-версии); (5) optional по умолчанию (proto3 — все поля optional — Protobuf — «поле отсутствует — значение по умолчанию» — контракты); версионирование (стратегии): (1) эволюция схемы (добавлять поля, не ломать — Protobuf — «один .proto эволюционирует» — API-версии — предпочтительно); (2) версии в package (package v1/v2 — Protobuf — «package v2 — новый .proto» — API-версии — для breaking changes); (3) версии в имени файла (user_v1.proto/user_v2.proto — Protobuf — «v1 и v2 рядом» — API-версии — миграция); когда Protobuf: gRPC (gRPC — Protobuf — стандарт — gRPC), кросс-языковые API (один .proto — много языков — gRPC — контракты), внутренние сервисы (типизация/эволюция — микросервисы — «внутренние API на Protobuf» — gRPC); когда НЕ Protobuf: публичные API (JSON/REST — проще для внешних — REST — «Protobuf для публичного API» — сложно), простые случаи (JSON достаточно — REST — «Protobuf для простого API» — оверхед — техдолг); антипаттерны: смена номеров полей (breaking — API-версии), смена типов (breaking — API-версии), переиспользование reserved-номеров (breaking — Protobuf — «reserved 3 — не использовать 3 снова» — API-версии), breaking без версий (сломал схему — клиенты упали — API-версии — «breaking без v2» — техдолг).
Практика: эволюция схемы без breaking changes
Добавление полей (безопасно): message User { string name = 1; int32 age = 2; string email = 3; // новое } (Protobuf — «добавил email = 3» — старые клиенты игнорируют — API-версии); Удаление полей (безопасно с reserved): message User { string name = 1; // int32 age = 2; // удалено reserved 2; reserved "age"; } (Protobuf — «удалил age, зарезервировал 2» — не переиспользовать — API-версии); Переименование полей (безопасно — имя не в бинарном формате): message User { string full_name = 1; // было name } (Protobuf — «переименовал name → full_name» — номер 1 тот же — старые данные парсятся — API-версии); Изменение типа (breaking — нужна версия): // v1: message User { string age = 1; } // v2: message User { int32 age = 1; } (Protobuf — «сменил тип — breaking» — package v2 — API-версии); Schema registry (контроль совместимости): gRPC — schema registry (Avro/Protobuf — gRPC — «несовместимая схема — отклонена» — контракты — CI проверяет совместимость — API-версии); Миграция v1 → v2 (постепенная): (1) создать v2 (package v2 — Protobuf), (2) сервер поддерживает v1+v2 (gRPC — «сервис на v1 и v2» — API-версии), (3) клиенты мигрируют постепенно (API-версии — «клиенты переходят на v2» — gRPC), (4) удалить v1 (все на v2 — API-версии — «v1 удалён» — техдолг).
Практический сценарий: эволюция user.proto
Контекст: сервис пользователей на gRPC (gRPC), схема user.proto (message User { string name = 1; int32 age = 2; }), боль: нужно добавить email, но не сломать старых клиентов (API-версии); решение — эволюция схемы: (1) добавить поле (message User { string name = 1; int32 age = 2; string email = 3; // новое } — Protobuf — «добавил email = 3» — API-версии); (2) сгенерировать код (protoc --python_out=. --go_out=. user.proto — gRPC — «схема → код» — контракты); (3) обновить сервер (сервер заполняет email — gRPC — «сервер отдаёт email» — контракты); (4) старые клиенты (игнорируют email — Protobuf — «старый клиент не знает email — игнорирует» — API-версии — не ломаются); (5) новые клиенты (используют email — gRPC — «новый клиент читает email» — контракты); (6) позже — удалить age (устарело): message User { string name = 1; reserved 2; reserved "age"; string email = 3; } (Protobuf — «удалил age, зарезервировал 2» — API-версии — не переиспользовать 2); (7) CI-проверка совместимости (gRPC — schema registry — «несовместимая схема — отклонена» — контракты — API-версии); результат: email добавлен без breaking (API-версии — старые клиенты работают — Protobuf), age удалён безопасно (reserved — Protobuf — API-версии), CI ловит breaking (schema registry — контракты — API-версии); вывод: Protobuf-эволюция без breaking (добавлять/удалять с reserved — Protobuf — API-версии — ADR — «смена номера/типа» — breaking — техдолг).
FAQ
Protobuf против JSON — когда что использовать? Protobuf: бинарный, типизированный, компактный (Protobuf — gRPC — «меньше JSON» — ёмкость), JSON: текстовый, человекочитаемый, универсальный (REST — «JSON проще» — REST); когда Protobuf: gRPC (gRPC), кросс-языковые API (gRPC — контракты), внутренние сервисы (микросервисы — gRPC), производительность важна (ёмкость — «Protobuf быстрее» — gRPC); когда JSON: публичные API (REST — «JSON для внешних» — REST), простые случаи (REST — «JSON достаточно» — техдолг), отладка (JSON человекочитаемый — REST — «JSON проще отлаживать» — REST); «Protobuf везде» — антипаттерн (публичные API — JSON проще — REST — ADR — техдолг).
Что такое reserved и зачем он нужен? Reserved — резервирование номеров/имён удалённых полей (Protobuf — «reserved 2; reserved "age";» — API-версии); зачем: предотвратить переиспользование (удалил поле 2 — зарезервировал — не использовать 2 снова — Protobuf — «reserved 2 — не добавлять поле 2» — API-версии); без reserved: переиспользовал номер 2 — старые данные не парсятся (Protobuf — «удалил 2, добавил 3 как 2» — старые клиенты ломаются — API-версии); практика: всегда reserved при удалении (Protobuf — «удалил поле — зарезервировал» — API-версии — контракты); «reserved не нужен» — миф (без reserved — переиспользование — breaking — API-версии — ADR — техдолг).
Как версионировать Protobuf-схемы? Стратегии: (1) эволюция схемы (добавлять/удалять с reserved — Protobuf — «один .proto эволюционирует» — API-версии — предпочтительно), (2) package v1/v2 (Protobuf — «package v2 — новый .proto» — API-версии — для breaking), (3) файл v1/v2 (Protobuf — «user_v1.proto/user_v2.proto» — API-версии — миграция); когда эволюция: обратная совместимость возможна (Protobuf — «добавить поле — ок» — API-версии), когда v2: breaking changes (смена типа/номера — Protobuf — API-версии); миграция v1→v2: сервер v1+v2, клиенты мигрируют, удалить v1 (API-версии — gRPC — «постепенная миграция» — контракты); «v2 сразу» — антипаттерн (эволюция проще — Protobuf — API-версии — ADR — техдолг).
Как проверить совместимость Protobuf-схем? Инструменты: schema registry (gRPC — Avro/Protobuf registry — контракты — «несовместимая схема — отклонена» — API-версии), protoc-gen-validate (Protobuf — валидация схем — контракты), CI-проверка (gRPC — «CI проверяет совместимость» — контракты — API-версии); правила: не менять номера/типы (Protobuf — breaking — API-версии), reserved при удалении (Protobuf — API-версии), добавлять — ок (Protobuf — API-версии); «совместимость не проверяется» — риск (breaking в проде — клиенты упали — API-версии — incident — CI/schema registry обязательны — контракты — ADR — техдолг).
Источники: Protobuf docs (proto3, compatibility), gRPC docs — ссылки в шапке.