Reading — step 1 of 5
Read
~1 min readCode Generation
Schema Evolution
Protobuf's superpower: add fields without breaking old code.
Rules:
- Don't change a field's tag number.
- Don't change a field's wire type (e.g. int32 → string).
- Don't reuse a tag number for a different field.
- New fields must use new tags.
- Required fields are forbidden in proto3 (everything optional).
Adding a field:
// Before:
message User {
string name = 1;
int32 age = 2;
}
// After:
message User {
string name = 1;
int32 age = 2;
string email = 3; // new
}
- Old client + new server: client sends without email. Server sees email = "" (default).
- New client + old server: client sends email; server skips unknown tag.
Removing a field:
- Mark
reserved:
message User {
reserved 1; // ex-name
reserved "name"; // also reserve the name
int32 age = 2;
}
- Prevents accidental reuse.
Renaming:
- Change name; keep tag.
- Wire format unchanged.
- Code that referenced old name breaks.
Changing required → optional (proto2 only):
- Was a hard rule; allowed now.
Adding to enum:
- New value at end. Old code sees as default (0 if first, else "unknown").
- Older proto requires reserved enum names too.
Compatibility matrix (write new, read old or vice versa):
- New field: skipped. Old works.
- Removed field: not present. New code must handle.
- Type change in tag: undefined behavior. AVOID.
Tools:
buf(modern protobuf manager): lints + breaking-change detection.protoc-gen-buf-breaking: CI check.
Best practice: never break compatibility once deployed.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…