Суть Читай реальные .proto-сниппеты — правки field tag, reserved, сигнатуры streaming — и предсказывай поведение на проводе или фикс с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Схема Protobuf — это контракт, который ты читаешь на код-ревью, до того как порча уедет в прод. Прочитай каждый diff или сигнатуру, разберись, что это делает на проводе, и выбери изменение, которое senior реально одобрил бы.
Цель
Потренируй ревью, которое ты прогоняешь на каждом proto-PR: замечай ловушки field tag, подтверждай, что удаление корректно reserved, и читай сигнатуру streaming на ту форму вызова, которую она реально объявляет.
Старый клиент записал Order с priority в поле 4. Сервис, задеплоенный с этой новой схемой, читает то сообщение. Что он видит и почему?
Heads-up Имена никогда не едут по проводу; едут только номера. Старые байты priority помечены 4, а поле 4 теперь coupon, так что значение декодируется как coupon.
Heads-up Декодирование не сравнивает со старым типом — оно читает байты с провода под текущую схему. Здесь нет ошибки несовпадения типов, лишь значение, направленное в неверное поле.
Heads-up Переиспользование тех же номеров под другие поля — это и есть ловушка, а не защита. Wire-совместимость — это идентичность по номеру, а не наличие номера.
Предложено новое поле: вернуть `string email = 2;`, потому что команда снова хочет email. Это компилируется и это правильный ход?
Heads-up reserved принудительно проверяется protoc — объявить reserved номер или имя и затем использовать его — это ошибка компиляции. Эта проверка и есть вся причина резервировать при удалении.
Heads-up Совпадение типов не важно; старые пиры, всё ещё шлющие исходное поле 2, запишут в новый email независимо от этого. reserved существует, чтобы сделать это невозможным.
Heads-up Снос резервации ради переиспользования номера сводит на нет механизм безопасности — именно эту правку reserved и должен помешать одобрить ревьюеру.
Сопоставь каждый RPC с его формой вызова и положением ключевого слова stream.
Heads-up Ключевое слово stream меняет форму: stream Message означает последовательность Message, а не одно. Unary — только RPC без stream ни на одной стороне.
Heads-up stream на возвращаемом типе (втором) означает, что сервер стримит ответы — server streaming. Client streaming ставит stream на типе запроса.
Heads-up Bidirectional streaming — это один RPC со stream и на запросе, и на ответе — ровно то, что объявляет Live. Он едет по одному мультиплексированному HTTP/2-потоку.
Клиент шлёт UpdateProfile с bio равным пустой строке и опущенным marketing_opt_in. Сервер хочет сделать PATCH только тех полей, которые клиент реально намеревался изменить. Что схема даёт ему различить?
Heads-up У обычных скаляров proto3 нет presence: пустая строка неотличима от невыставленной. Только optional (или поля-сообщения) несут presence-бит.
Heads-up proto3 вернул явное presence через ключевое слово optional; marketing_opt_in здесь его несёт. Нельзя различить как раз необъявленное optional bio.
Heads-up Скаляр-пустая-строка сериализуется так же, как default'нутый/неприсланный — сервер не может вывести намерение из него. Эта двусмысленность — как раз почему PATCH-эндпоинты берут optional или FieldMask.
Итог
Ревью Protobuf — это чтение номеров, а не имён: diff с перенумерацией молча направляет старые значения в неверное поле, поэтому эволюция должна быть append-only с reserved, охраняющим удаления (а protoc принуждает резервацию). Сигнатуру streaming читают по тому, какая сторона несёт stream — запрос для client, ответ для server, обе для bidi. А presence в proto3 реален, но opt-in: обычные скаляры не отличают unset от default, поэтому PATCH-стиль API тянется к optional или FieldMask. Лови это на ревью, до того как порча задеплоится.