Protocol Buffers & Proto3
You'll learn to
- -Write a basic proto3 message and service definition, and explain what field numbers actually do
- -Explain why gRPC's binary, strongly-typed contract trades human-readability for performance and strict correctness
REST and GraphQL both send data as human-readable text (typically JSON) over standard HTTP. gRPC takes a different approach: Protocol Buffers (protobuf) define a strongly-typed schema that gets compiled into a compact binary wire format, and the RPC calls themselves run over HTTP/2 - trading human-readability for smaller payloads, faster serialization, and a contract the compiler enforces rather than one only convention enforces.
A Proto3 Message
syntax = "proto3";
message User {
int64 id = 1;
string name = 2;
string email = 3;
bool is_verified = 4;
}A `message` is protobuf's equivalent of a REST DTO or a GraphQL type - a structured, typed shape. Field types are explicit (`int64`, `string`, `bool`), and unlike JSON, there is no ambiguity about whether a number is an integer or a float, or how large an integer can be - the type is part of the contract, checked at compile time in every language protobuf generates code for.
Field Numbers: Not Cosmetic
The `= 1`, `= 2`, `= 3` after each field are not documentation or ordering hints - they are the actual identifier used in the binary wire format. Protobuf doesn't send field names over the wire at all (that's a large part of why the format is so compact compared to JSON, which repeats every key name in every message) - it sends the field number and the value, and the receiving side's generated code maps that number back to the correct field.
message User {
int64 id = 1;
string name = 2;
// string legacy_username = 3; // REMOVED - but field number 3 must never
// be reused for something else, since old
// binary-encoded messages might still have
// data tagged with field number 3 floating
// around (in logs, queues, caches)
reserved 3; // explicitly reserves it, so a future
// engineer can't accidentally reuse it
string phone = 4;
}Since the wire format only carries the number, reusing a retired field number for a new, differently-typed field means old serialized data (still tagged with that number, sitting in a log or a queue somewhere) could be misinterpreted as the new field's type when deserialized later - `reserved` makes the retirement explicit and prevents that mistake at compile time, rather than relying on every future engineer remembering not to reuse it.
A Service Definition
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc CreateUser (CreateUserInput) returns (User);
}
message GetUserRequest {
int64 id = 1;
}A `service` declares the actual RPC methods a gRPC server exposes, each with an explicit request and response message type - this is the direct analog of REST's endpoints or GraphQL's Query/Mutation fields, but here the compiler generates client and server stub code directly from this definition in whatever language each side is written in, so the client-side call looks like an ordinary local function call rather than something the developer manually serializes and sends.
Because protobuf generates real client/server code from the `.proto` file, the contract enforcement happens at compile time in strongly-typed languages - a client written in Go calling a Python server gets a compile error for a type mismatch, not a runtime surprise the way an equivalent mistake in a loosely-typed JSON REST call often would.
Interview Signal is part of Pro
See a real weak answer next to a real strong one for this exact topic.
Quiz is part of Pro
Test what you just read with a short quiz, and bank the XP.
Design Proto Origins in the API Design Lab's gRPC & Event-Driven act.