ASP.NET Core › gRPC Services

How would you design a gRPC-based service to gracefully handle protobuf schema evolution (adding/removing fields) without breaking existing deployed clients?

Advanced

Answer

Protobuf's field-numbering system (each field has a unique numeric tag, not just a name) is specifically designed for backward/forward compatibility: adding a new field with a new, previously unused number is safe (old clients simply ignore it, new clients see a default value from old servers), removing a field requires marking its number as reserved (via the reserved keyword) to prevent future reuse causing data corruption, and field numbers/types must never be changed or reused for existing fields once deployed -- following these rules lets services and clients be deployed and upgraded independently without coordinated releases.
message Item {
  reserved 3;  // formerly "legacy_price" field, now permanently retired
  reserved "legacy_price";

  int32 id = 1;
  string name = 2;
  // field 3 intentionally skipped/reserved
  int32 price_cents = 4;  // new field, added safely with an unused number
}
Real-world example A payments service adds a new price_cents field (replacing a deprecated legacy_price field, whose number 3 is marked reserved) to its protobuf contract, allowing older client versions still expecting the old schema to keep working unaffected while newer clients adopt the new field, all without requiring a coordinated simultaneous deployment.

Common follow-ups: What happens if you accidentally reuse a reserved field number in the future?;How does protobuf handle enum evolution compatibility differently from message field evolution?