Skip to content
Protobuf Schema Evolution: Evolving gRPC Contracts Safely 3 min

Protobuf Schema Evolution: Evolving gRPC Contracts Safely

SD
ScaleDojo
May 11, 2026
3 min read634 words
Protobuf Schema Evolution: Evolving gRPC Contracts Safely

The Mobile App That Could Not Update

Your gRPC backend serves an iOS app. Apple takes 2-7 days to review app updates. Meanwhile, you need to ship a backend change that adds new fields to a response and removes an old one. But 40% of your users are still on the old app version. If you break the wire format, those users see crashes. Protobuf schema evolution rules tell you exactly which changes are safe and which are not.

How Protobuf Wire Format Works

Protobuf serializes by FIELD NUMBER, not field name:

  message User {
    string name = 1;     // field number 1
    string email = 2;    // field number 2
    int32 age = 3;       // field number 3
  }

  Wire format (binary):
  [field_num=1][type=string][value='Alice']
  [field_num=2][type=string][value='alice@x.com']
  [field_num=3][type=int32][value=30]

  Field NAMES are only for human readability.
  The wire only sees NUMBERS and TYPES.
  This is why renaming a field is safe but
  changing its number is catastrophic.

Safe Changes (Backwards Compatible)

SAFE - Adding a new field:
  // v1 (old client knows this)
  message User {
    string name = 1;
    string email = 2;
  }

  // v2 (new server adds field 3)
  message User {
    string name = 1;
    string email = 2;
    string avatar_url = 3;  // NEW - old clients ignore it
  }
  // Old client sees unknown field 3 -> skips it. No crash.
  // New client sees field 3 -> uses it. Everyone happy.

SAFE - Renaming a field:
  // Wire format uses numbers, not names
  string user_name = 1;  -> string full_name = 1;  // SAFE

SAFE - Adding a new RPC method:
  // Old clients never call it, so they are unaffected

SAFE - Adding a new enum value:
  // Old clients treat unknown enum values as default (0)

Breaking Changes (NEVER Do These)

BREAKING - Changing a field number:
  string name = 1;   ->  string name = 5;  // DISASTER
  // Old clients send field 1, new server expects field 5
  // Data silently disappears or gets misinterpreted

BREAKING - Changing a field type:
  int32 age = 3;     ->  string age = 3;   // DISASTER
  // Binary encoding for int32 and string are incompatible
  // Deserialization fails or produces garbage

BREAKING - Removing a field without reserving:
  // v1: string email = 2;
  // v2: (removed email)
  // v3: int32 login_count = 2;  // REUSED field 2!
  // Old clients sending email string into int32 field = crash

CORRECT way to remove:
  message User {
    string name = 1;
    reserved 2;                 // lock field number forever
    reserved 'email';           // lock field name forever
    string avatar_url = 3;
  }

Schema Evolution Cheat Sheet

Change                  Safe?    Why
----------------------  -------  ---------------------------
Add new field           YES      Old clients skip unknown
Rename field            YES      Wire uses numbers, not names
Add new RPC             YES      Old clients do not call it
Add enum value          YES      Unknown -> default (0)
Change field number     NO       Breaks all existing clients
Change field type       NO       Wire encoding incompatible
Remove field (no rsrv)  NO       Number might be reused
Remove field (reserved) YES      Number locked, safe
Change optional->req    NO       Deserialization failures

Interview Tip

When discussing gRPC API evolution, say: 'Protobuf uses field numbers for wire serialization, so renaming fields is always safe. Adding new fields is the primary evolution mechanism - old clients simply ignore unknown fields. I would never reuse a deleted field number - instead I mark it reserved forever. For major breaking changes, I would create a new service version (v2.UserService) rather than modifying the existing contract. This lets me roll out changes gradually while old clients continue working.'

<
Protobuf Schema Evolution: Evolving gRPC Contracts Safely - Architecture Diagram
Architecture overview
/blockquote>

Key Takeaway

Protobuf uses field numbers (not names) for serialization. Adding fields is safe. Changing types or numbers is breaking. Never reuse deleted field numbers - reserve them. These rules let you evolve gRPC services without coordinated client-server deployments.

Enjoyed this article?

Share it with your network to help others level up their system design skills.

Discussion0

Join the Discussion

Sign in to leave comments, reply to others, or like insights.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!

Related Articles

Enjoyed this content?

Your support keeps us creating free resources

We put a lot of hours into researching and writing these guides. If it helped you, consider buying us a coffee. Every bit goes toward keeping ScaleDojo's content free and growing.

$

One-time payment via Stripe. ScaleDojo account required.

ScaleDojo Logo
Initializing ScaleDojo