Reserved Fields and Field Number Management
Manage field numbers and names with reserved declarations. Learn why field number reuse is dangerous, how to deprecate fields safely, and best practices for schema evolution.
Detailed Explanation
Safe Schema Evolution with Reserved Fields
When you remove a field from a protobuf message, you must prevent its field number and name from being reused. Reusing a field number with a different type causes data corruption when old and new code interact.
syntax = "proto3";
message UserProfile {
// Current fields
int64 id = 1;
string username = 2;
string email = 5;
string avatar_url = 8;
bool is_verified = 9;
// Fields 3, 4, 6, 7 were removed in past versions
reserved 3, 4, 6, 7;
reserved "first_name", "last_name", "phone", "address";
// Ranges are also supported
// reserved 100 to 199;
}
Why Reserved Matters
Consider this scenario without reserved:
- Version 1:
string phone = 3;— field 3 is a string containing a phone number - Version 2: Phone field removed, developer adds
int32 age = 3;— field 3 is now an integer - A message serialized by Version 1 is read by Version 2: the phone string is interpreted as an integer, causing corruption or crashes
Using reserved 3; prevents the compiler from allowing field number 3 to be reused, catching this error at compile time.
Reserved Names vs. Numbers
- Reserved numbers prevent wire-level corruption (most critical)
- Reserved names prevent JSON format conflicts and source code confusion
- Best practice: reserve both the number and name of every removed field
Field Number Ranges
Field numbers are a finite resource:
| Range | Purpose |
|---|---|
| 1-15 | Single-byte tag encoding — use for frequently accessed fields |
| 16-2047 | Two-byte tag encoding — standard fields |
| 2048-536,870,911 | Three or more bytes — rarely used |
| 19000-19999 | Reserved by the protobuf implementation — cannot use |
Deprecation Pattern
Instead of removing fields immediately, mark them as deprecated first:
message Config {
string name = 1;
string old_setting = 2 [deprecated = true];
string new_setting = 3;
}
Use Case
Evolving production protobuf schemas safely in large organizations where multiple service versions coexist, and backward/forward compatibility is essential to avoid data corruption during rolling deployments.