Kinoko's TIL Log

Protobuf Reserved Fields & API Versioning

The Point

When removing a response field from a protobuf message, you cannot simply delete it – you must mark it as reserved. Otherwise, if a future field reuses the same field number, old clients will misinterpret the new field’s value as the old field.

Explanation

Why is reserved needed?

Protobuf identifies data on the wire by field number, not field name. So when you:

  1. Originally have string old_field = 3; in a response message
  2. Integrate a new external service, the backend logic changes, this field is no longer populated, so you delete it
  3. Later add int32 new_field = 3; (reusing number 3)

At this point, clients still using the old proto definition will receive the response and try to read new_field’s value as old_field – the type mismatch causes a crash.

Correct approach: mark it as reserved

1message MyResponse {
2  reserved 3, 5;                    // reserve these field numbers from reuse
3  reserved "old_field", "another";  // also reserve the field names (prevents accidental reuse in code)
4
5  string active_field = 1;
6  int32 other_field = 2;
7}

With reserved in place, any attempt to reuse those numbers or names will cause a protoc compilation error.


What is the relationship between v2alpha and v2beta?

These are API version stability labels, following Google AIP (API Improvement Proposals) naming conventions, commonly seen in gRPC / proto package naming:

VersionMeaning
v2alpha / v2alpha1Experimental; may change drastically or be removed at any time; no backward compatibility guarantee
v2beta / v2beta1Feature is mostly finalized, but details may still change; usually has compatibility commitments but incomplete
v2Stable release; full backward compatibility guarantee

The typical progression is: v2alpha1 -> v2alpha2 -> v2beta1 -> v2beta2 -> v2

How it looks in proto files:

1// Experimental version
2package mycompany.myservice.v2alpha1;
3
4// Feature-complete but still being polished
5package mycompany.myservice.v2beta1;
6
7// Stable release
8package mycompany.myservice.v2;

Different versioned packages are completely independent namespaces that can coexist, allowing old and new clients to each use their corresponding version.

Knowledge Sugar

Two ways to write reserved, and they can be separate or combined:

1reserved 1 to 3;       // contiguous range
2reserved 1, 2, 3;      // individual listing
3reserved "foo", "bar"; // reserve names (recommended to do both, prevents typo reuse)

Difference from deprecated:

Practical mindset when designing new proto contracts:

Further reading:

#grpc #api-design #til

← Back to Main Page