How does gRPC handle backward and forward compatibility in protobuf schemas?
How gRPC and Protocol Buffers keep schemas compatible: numbered fields, optional additions, reserved numbers, and the changes that break compatibility.
Expected Interview Answer
gRPC relies on Protocol Buffers' wire format, which identifies fields by numeric tags rather than names, so old and new code can exchange messages safely as long as you follow the evolution rules: never reuse or change existing field numbers, add new fields as optional, and let unknown fields pass through untouched.
Backward compatibility means new code can read messages produced by old code; forward compatibility means old code can read messages from new code by ignoring fields it does not recognize. Protobuf achieves both because each field is keyed by its tag number and wire type. Removed fields should be marked 'reserved' so their numbers and names are never reused, and unset fields simply take default values. Breaking changes come from renumbering fields, changing a field's type incompatibly, or reusing a deleted field number.
- Old and new services interoperate without lockstep deploys
- Unknown fields are preserved or safely ignored
- Enables independent, rolling upgrades across services
- Reserved fields prevent accidental number reuse
- Schema evolves without breaking existing clients
AI Mentor Explanation
Think of a scorebook where each stat lives in a numbered column rather than by heading. A new edition adds a 'dot-ball percentage' column at number 15; old scorers simply skip column 15 and still read runs and wickets correctly. Protobuf's numbered fields work identically, so new tags are ignored by old readers while both versions still understand the innings.
Step-by-Step Explanation
Step 1
Keep field numbers stable
Never change or reuse an existing field's tag number; the number, not the name, identifies the field on the wire.
Step 2
Add new fields as optional
Introduce new fields with fresh, unused numbers; unset values fall back to defaults so old producers stay valid.
Step 3
Reserve removed fields
When deleting a field, mark its number and name 'reserved' so they can never be accidentally reused later.
Step 4
Avoid incompatible type changes
Do not switch a field to an incompatible type; only compatible changes are safe on the wire.
Step 5
Preserve unknown fields
Rely on the runtime keeping or ignoring unrecognized fields so old code round-trips messages from newer schemas.
What Interviewer Expects
- Explains fields are identified by number, not name
- Distinguishes backward from forward compatibility
- Knows to add fields as optional with new numbers
- Uses 'reserved' for removed field numbers and names
- Identifies breaking changes like renumbering or type changes
Common Mistakes
- Reusing a deleted field's number for a new field
- Renumbering existing fields when refactoring
- Changing a field's type to an incompatible one
- Assuming renaming a field breaks the wire format (it does not)
- Not reserving removed field numbers, risking future reuse
Best Answer (HR Friendly)
“gRPC uses Protocol Buffers, which label each piece of data with a number instead of a name, so newer and older versions of a service can still talk to each other. As long as you only add new numbered fields and never reuse old numbers, teams can upgrade their services at different times without breaking anything.”
Code Example
// Version 1
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
// Version 2 - compatible evolution
message User {
int32 id = 1;
string name = 2;
reserved 3; // email removed; number never reused
reserved "email";
string phone = 4; // new field, fresh number, optional
string country = 5; // old clients ignore this
}Follow-up Questions
- Why does protobuf identify fields by number instead of name?
- What does the 'reserved' keyword do and why use it?
- Which schema changes are considered breaking?
- How does a client handle fields it does not recognize?
- Is renaming a protobuf field a breaking change on the wire?
MCQ Practice
1. How does protobuf identify a field on the wire?
Each field is encoded with its numeric tag and wire type, so names can change without breaking compatibility but numbers must not.
2. What should you do when removing a field from a message?
Reserving the removed field's number and name prevents future accidental reuse that would corrupt older readers.
3. Which change is a breaking change?
Reusing a deleted field number for an incompatible field breaks compatibility because old data keyed to that number is misinterpreted.
Flash Cards
Backward vs forward compatibility? — Backward: new code reads old messages. Forward: old code reads new messages by ignoring unknown fields.
What identifies a protobuf field on the wire? — Its numeric tag and wire type, not its name, so field numbers must stay stable.
Why use 'reserved'? — It blocks a removed field's number and name from being reused, preventing future incompatibility.
Name a breaking change. — Renumbering a field, reusing a deleted number, or changing a field to an incompatible type.