Skip to content
API Design Learn/gRPC Fundamentals
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Evolving the Wire Format

7 min read

You'll learn to

  • -Apply protobuf's backward/forward compatibility rules when adding, removing, or changing fields
  • -Use `oneof` correctly for a field that can hold exactly one of several possible types

Protobuf was specifically designed with schema evolution in mind - old clients and new servers, or new clients and old servers, need to keep working together during a rolling deployment, the exact same problem the REST versioning chapter covered, just with protobuf's own specific compatibility rules.

What's Safe to Change

  • -Safe: adding a new field with a new, never-used field number. Old code simply ignores fields it doesn't recognize.
  • -Safe: removing a field, as long as its number is marked `reserved` so it's never accidentally reused.
  • -Unsafe: changing a field's number. The wire format identifies fields by number, not name - changing it is indistinguishable from removing the old field and adding an unrelated new one.
  • -Unsafe: changing a field's type to an incompatible one (e.g. `string` to `int32`). Some type changes within the same "wire type" family are safe (`int32` to `int64`); most are not.
  • -Generally safe: renaming a field. Since the wire format only carries the number, a rename doesn't affect already-compiled binaries - but it does affect any human-readable serialization (like protobuf's JSON mapping) or generated code relying on the field's name.

oneof: Exactly One of Several Types

A field that holds exactly one of several possible shapes
message PaymentMethod {
  oneof method {
    CreditCard credit_card = 1;
    BankTransfer bank_transfer = 2;
    DigitalWallet digital_wallet = 3;
  }
}

`oneof` declares that exactly one of the listed fields is set at a time - setting `credit_card` automatically clears `bank_transfer` or `digital_wallet` if either was previously set, and generated code exposes a way to check which one is currently set. This is protobuf's answer to a union type: without `oneof`, a message with three optional fields representing mutually exclusive alternatives leaves it ambiguous (and possible, incorrectly) for more than one to be set simultaneously.

Adding a New oneof Option

Extending a oneof is additive, as long as the new field number is unused
message PaymentMethod {
  oneof method {
    CreditCard credit_card = 1;
    BankTransfer bank_transfer = 2;
    DigitalWallet digital_wallet = 3;
    Cryptocurrency cryptocurrency = 4;   // new option, safe to add
  }
}

Adding a new option to an existing `oneof` is additive and safe, following the same "new field, new number" rule as any other field - old clients that don't know about `cryptocurrency` simply can't construct or recognize a message using that option, but they aren't broken by its existence, they just don't support it yet.

A common mistake: reordering fields in the .proto file, assuming it's purely cosmetic. It genuinely is cosmetic for the wire format (which only cares about field numbers), but reordering can still confuse a human reader comparing an old and new version of the schema - keep the numeric field numbers as the actual source of truth, not the field's position in the file.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

Ready to Build This?

Design Wire Evolution in the API Design Lab's gRPC & Event-Driven act.

ScaleDojo Logo
Initializing ScaleDojo