Articles

What Are Protocol Buffers? Protobuf Explained

Protocol Buffers (protobuf) are a binary serialization format with strict schemas — smaller and faster to parse than JSON. How it works.

Chisato Chisato · · 4 min read
Abstract illustration of API connections

Protocol Buffers, or protobuf, are Google’s language-neutral binary serialization format: you define a message’s fields in a .proto schema file, a compiler generates typed code for your language, and messages are encoded as compact binary blobs instead of text. The payoff over formats like JSON is size and parse speed — at the cost of human readability and a required schema.

How a .proto schema works

A .proto file declares message types and their fields, each with a type and a unique field number:

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
}

The field numbers, not the names, are what actually go on the wire. Run the protobuf compiler (protoc) against this file and it generates a class or struct in your target language — Go, Python, Java, TypeScript, and more — with getters, setters, and serialization methods already written. You never hand-write a parser.

Encoding: binary and compact

A JSON payload spells out every key as a string on every message: {"id": 42, "name": "Ana"}. Protobuf instead writes each field as a tag (derived from the field number and wire type) followed by its value, with no field names and no punctuation. Integers use variable-length encoding, so small numbers take fewer bytes. The result is typically a fraction of the size of the equivalent JSON, and decoding is a matter of reading tagged bytes rather than parsing text — no tokenizer, no string allocation for keys.

The trade-off is that a protobuf message is unreadable without its schema. You can’t open one in a text editor and understand it the way you can with JSON; you need the .proto file (or a tool that has it) to make sense of the bytes.

Protobuf vs JSON

ProtobufJSON
FormatBinaryText
SchemaRequired (.proto file)Optional (JSON Schema)
Payload sizeSmallerLarger
Human-readableNoYes
Parsing speedFasterSlower
ToolingCompiler-generated codeNative in every language
DebuggingNeeds a decodercurl and read it

Where protobuf shows up

Protobuf is the default wire format for gRPC, which pairs it with HTTP/2 for typed, streaming service-to-service calls. It’s common in internal microservice fleets where every service is written by people who control both ends of the wire and can regenerate code when a schema changes. It’s less common on public-facing REST APIs, where JSON’s readability and universal support in browsers win out — nobody wants to force every API consumer to install a protobuf compiler just to make a request.

Protobuf isn’t limited to RPC, either. It’s also used as a storage and interchange format on its own — anywhere you want a compact, versioned binary representation of structured data without hand-rolling a parser.

Schema evolution and versioning

Protobuf was designed to let schemas change without breaking old clients or servers. The rules that make this safe:

  • Never reuse a field number. Once a number has shipped, it’s permanently tied to that field’s meaning, even if you later remove the field.
  • New fields are safe to add. Old code simply ignores fields it doesn’t recognize; new code treats missing fields as unset (or their default value).
  • Don’t change a field’s type once it’s shipped — that changes how the bytes are interpreted and breaks compatibility.
  • Removing a field is safe as long as you don’t reuse its number later. Mark it reserved in the .proto file to prevent that mistake.

This forward- and backward-compatibility is a big part of why protobuf is popular for long-lived internal APIs: services can be deployed independently and out of order without a synchronized rollout.

When JSON is still the better choice

Protobuf isn’t a universal upgrade. Reach for JSON instead when:

  • The API is public and consumers you don’t control need to inspect payloads without tooling.
  • Payloads are small and infrequent enough that size and parse speed don’t matter.
  • You want to curl an endpoint and read the response directly, which matters a lot during debugging and incident response.
  • Your stack is browser-heavy — JSON.parse is built in everywhere; protobuf needs a JS library and generated code.

Plenty of systems end up using both: protobuf for internal service-to-service traffic where speed and schema discipline matter, JSON at the public edge where compatibility and readability matter more.

The takeaway

Protocol Buffers trade human readability for size and speed: a .proto schema compiles to generated code, messages are encoded as compact tagged binary instead of text, and disciplined field-numbering keeps old and new versions compatible. That makes it a strong default for internal RPC — especially with gRPC — and a poor fit for public APIs where anyone should be able to read a response without a decoder.

Takina Takina · · 4 min read

Node.js Buffer Explained: Binary Data Handling

Node's Buffer class holds raw binary data outside the V8 heap, letting Node handle files, sockets, and streams efficiently. Here's how it works.

#JavaScript #Backend #Developer Tools
Chisato Chisato · · 4 min read

Token Bucket vs Leaky Bucket Rate Limiting

Token bucket allows bursts up to a cap; leaky bucket smooths traffic to a constant rate. How each rate-limiting algorithm works and when to pick it.

#Backend #Distributed Systems #Developer Tools