Skip to content
Schema Evolution
step 1/5

Reading — step 1 of 5

Read

~1 min readCode Generation

Schema Evolution

Protobuf's superpower: add fields without breaking old code.

Rules:

  1. Don't change a field's tag number.
  2. Don't change a field's wire type (e.g. int32 → string).
  3. Don't reuse a tag number for a different field.
  4. New fields must use new tags.
  5. 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…