Protocol Buffers
ActiveGoogle's language-neutral, platform-neutral mechanism for serializing structured data. Schemas defined in .proto files compile to language-specific code. Significantly smaller and faster than JSON for the same data.
In one line
Protocol Buffers (Protobuf) is Google's binary serialization format and IDL. .proto files define message schemas that compile to type-safe code in 10+ languages. Wire encoding uses 4 wire types (varint, 64-bit, length-delimited, 32-bit). Protobuf is the wire format for gRPC and is 3-10x smaller than equivalent JSON.
Quick Reference
| Field | Size | Description |
|---|---|---|
| Tag encoding | varint | tag = (field_number << 3) | wire_type. Field 1, wire type 0 = 0x08. |
| Wire type 0 | Varint | Variable-length integer. 7 bits per byte, MSB = continuation bit. Signed uses zigzag. |
| Wire type 1 | 64-bit | 8 bytes little-endian. Used for fixed64, sfixed64, double. |
| Wire type 2 | Length-delimited | Varint length prefix, then bytes. Used for string, bytes, embedded messages, packed repeated. |
| Wire type 5 | 32-bit | 4 bytes little-endian. Used for fixed32, sfixed32, float. |
| Field numbers | 1-536870911 | 1-15 encode in 1 byte (use for frequent fields). 16-2047 in 2 bytes. 19000-19999 reserved. |
| MIME types | 2 | application/protobuf (binary), application/x-protobuf (legacy). RFC 9996. |
Key Characteristics
Compact binary
3-10x smaller than JSON for equivalent data. Field names not transmitted – only field numbers.
Schema-first
.proto schema required for encoding and decoding. Breaking changes (removing fields, changing types) require field number management.
gRPC transport
gRPC uses Protobuf as its default serialization. HTTP/2 frames carry length-prefixed Protobuf messages.
Forward compatible
Unknown fields are preserved. Old clients can read new messages. New clients can read old messages. Field numbers must never be reused.
Message Format
// .proto schema definition
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page = 2;
int32 limit = 3;
}
// Wire encoding of SearchRequest{query:"foo", page:1, limit:10}
// Field 1 (query "foo"): 0x0a 0x03 0x66 0x6f 0x6f
// 0x0a = tag (field=1, wire_type=2=length-delimited)
// 0x03 = length 3
// 0x66 0x6f 0x6f = "foo"
// Field 2 (page 1): 0x10 0x01
// 0x10 = tag (field=2, wire_type=0=varint)
// 0x01 = varint 1
// Field 3 (limit 10): 0x18 0x0a
// 0x18 = tag (field=3, wire_type=0=varint)
// 0x0a = varint 10// Varint encoding examples:
// 1 = 0x01
// 127 = 0x7f
// 128 = 0x80 0x01 (two bytes: continuation bit set)
// 300 = 0xac 0x02
// Zigzag encoding (sint32/sint64):
// 0 → 0, -1 → 1, 1 → 2, -2 → 3
// n >= 0: (n << 1)
// n < 0: ((-n-1) << 1) | 1
// gRPC message framing:
// [1 byte compressed flag][4 bytes big-endian length][N bytes protobuf]