← Week 2: Custom Binary Protocols

Day 11: Protocol Versioning

Phase 3 · Jul 11, 2026

← Week 2: Custom Binary Protocols

Agenda (2–3 hours)

  • Read (45 min): Fielding's REST dissertation §5 (HATEOAS and versioning); Google API design guide on versioning; gRPC API design guide on backward compatibility
  • Study (45 min): Categorize these changes as breaking/non-breaking: add a field, remove a field, rename a field, change a field type, add an endpoint, remove an endpoint
  • Practice (45 min): Evolve a proto service through two backward-compatible changes and one breaking change; document the migration strategy
  • Challenge (30 min): Design a canary rollout strategy for a breaking protocol change in a service mesh
← Week 2: Custom Binary Protocols

Breaking vs Non-Breaking Changes

Non-breaking (safe):

  • Add a new optional field to a message
  • Add a new RPC/endpoint
  • Add a new enum value (if parsers use default for unknown)
  • Widen a numeric type (int32 → int64)

Breaking:

  • Remove or rename a field
  • Change a field type incompatibly
  • Remove an RPC/endpoint
  • Change required/optional semantics
  • Change field number (protobuf)
← Week 2: Custom Binary Protocols

Strategies

URL versioning (REST):

/v1/users  → old schema
/v2/users  → new schema

Both versions run simultaneously; clients migrate on their schedule.

Package versioning (gRPC):

package myapp.v1;  // stable
package myapp.v2;  // new version with breaking changes

Run both services simultaneously; route clients to v2 during migration.

Header versioning:

Accept: application/vnd.myapp.v2+json

One endpoint; version negotiated per request. More complex but single URL.

← Week 2: Custom Binary Protocols

Protobuf Forward Compatibility

Proto3 has built-in forward compatibility:

  • Unknown fields: preserved by default (proto3.5+). Old server receives a message with new fields → stores them → returns them in response (if the client uses passthrough)
  • Reserved fields: prevent reuse of removed field numbers
  • Default values: new fields default to zero/empty if absent from the wire

These properties let old clients talk to new servers and vice versa — as long as changes are additive.

← Week 2: Custom Binary Protocols

Rolling Deployments and Compatibility

In a rolling deployment, old and new versions of a service run simultaneously:

Version N:   [10 instances] ──→ deploys ──→ [5×v(N), 5×v(N+1)]
                                          ──→ [10×v(N+1)]

N reads N+1 response: must be backward-compatible (N+1 can add fields N ignores)
N+1 reads N response: N+1 must handle missing new fields gracefully

Both directions must work during the rollout window. Rule: make your changes non-breaking at the wire level, then clean up after all instances are upgraded.

← Week 2: Custom Binary Protocols

Key Takeaways

  • Non-breaking changes: add fields, add endpoints; never remove or rename
  • Protobuf's field numbering + reserved fields provide structural enforcement
  • Run multiple API versions simultaneously during migration
  • Rolling deployments require both old→new and new→old message compatibility

Tomorrow: zero-copy deserialization — bytes::Bytes, rkyv, and flatbuffers.