awesome-everything RU
↑ Back to the climb

APIs

gRPC and Protobuf: build a contract that survives evolution

Crux Hands-on project — design a gRPC service, evolve its schema across two incompatible versions without breaking live peers, add a streaming RPC, and prove deadline propagation under failure.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 240 min

Reading about field-number corruption is not the same as keeping a live contract intact across a deploy. Build a small gRPC service, then evolve its schema the way a real system forces you to — old clients still in flight, new fields landing, a removed field that must never come back — and prove with running peers that nothing decoded into the wrong slot.

Goal

Turn the unit’s mental model into a working contract: define a proto, run two service versions side by side, evolve the schema add-only with reserved, add a streaming RPC for the right reason, and demonstrate that deadlines propagate and cancel work across the call graph.

Project
0 of 7
Objective

Build a small gRPC service (Go, or any language with a mature gRPC stack), evolve its protobuf schema across two versions that must interoperate in both directions, add one streaming RPC, and prove — with two running peers and captured evidence — that schema evolution stays safe and deadlines propagate.

Requirements
Acceptance criteria
  • A reproducible compatibility matrix (commands + captured output) proving v1-v2 interop in both directions with zero misdecoded fields.
  • The deliberate-break branch's captured output showing a renumber/reuse corrupting a value, plus the reverted main where reserved blocks the reuse at compile time.
  • The streaming RPC works against a real client and the one-sentence justification names who streams and why the simpler shape was rejected.
  • A captured DEADLINE_EXCEEDED at the client for the slow-downstream call, with evidence the deadline propagated rather than each hop timing out independently.
Senior stretch
  • Add a CI gate using buf breaking (or protolock) that diffs the proto against the committed baseline and fails the build on any wire-incompatible change — renumber, type change, or unreserved removal.
  • Add a grpc-web or Connect path for a browser client and document exactly what the proxy/translation layer adds and which streaming shapes it cannot support.
  • Add a FieldMask (or optional fields) to support true PATCH semantics, and show the server distinguishing 'cleared to empty' from 'not sent' on a scalar field.
  • Benchmark the same payload as protobuf vs JSON for a numeric-heavy and a string-heavy message, and report the size/latency delta to confirm where the wire-format win is real.
Recap

This is the loop you run whenever a gRPC contract has to change under live traffic: evolve add-only, reserve every removal, and verify with two running peers that both directions still decode correctly — then prove to yourself, once, that a renumber really does corrupt so the discipline is muscle memory. Pick streaming shapes by who actually streams, set deadlines that propagate and cancel downstream work, and keep gRPC where it belongs — between your services, with REST/Connect at the browser edge.

Continue the climb ↑Why GraphQL gets N+1
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.