Protobuf Schema Design
Designing protobuf schemas that survive years of API evolution is a craft. This recipe distills the patterns Meridian uses internally to keep gateway payloads forward-compatible, backward-compatible, and resistant to the silent-drift bugs that plague long-lived RPC contracts.
1.Reserve field numbers aggressively
Field numbers are forever. Once a number ships to a single client, it can never be reused for a different semantic. Reserve removed fields explicitly so the compiler refuses to recycle them. Reserve numeric ranges for future extensions before you need them.
message Request {
string id = 1;
reserved 2, 3, 7 to 10;
reserved "legacy_token", "old_region";
string region = 4;
}2.Prefer wrapper types for optional scalars
Proto3 scalars cannot distinguish "unset" from "zero". For any field where the absence of a value is semantically different from the default, use the well-known wrapper types fromgoogle.protobuf. This keeps your gateway response objects unambiguous when downstream services need to merge partial updates or compute deltas.
3.Version through package paths, not field flags
When a breaking change is unavoidable, do not mutate the existing message in place. Publish a sibling package (meridian.v2) and let the gateway translate between them. Field flags like use_new_format always leak across service boundaries and become impossible to retire.