WierdX — Programming Reference All tutorials →
Developer reference · Practical tutorials · CS fundamentals
APIs & Integration

Serialization Formats Compared: JSON, Protocol Buffers, and Avro

Every message that crosses a network boundary has to be turned into bytes and back. Which format you pick decides how big those bytes are, how fast they parse, and how painfully your services break when one side changes its schema and the other hasn't caught up.

Published July 6, 2026

JSON's biggest advantage is also its biggest cost: every field name gets repeated, as text, in every single message. A payload with a field called customer_identifier pays for those seventeen characters on every message, forever, even though the receiving code already knows the schema. That repetition is exactly why JSON is so easy to debug — you can read a raw payload in a log file and understand it immediately — and exactly why it's not the most compact or fastest option once volume gets large.

Protocol Buffers: schema first, bytes second

Protobuf takes the opposite approach. You define a schema up front in a .proto file:

message Order {
  int64 id = 1;
  string customer_id = 2;
  double total = 3;
}

Each field gets a small integer tag (the = 1, = 2, = 3) instead of its name being written out, and values are packed using variable-length binary encoding rather than text. The result is typically 3-10x smaller than the equivalent JSON and meaningfully faster to parse, since there's no text scanning or number parsing from ASCII digits involved. The cost is that a protobuf message is unreadable without the schema that defines it — you can't open one in a log viewer and understand it the way you can JSON, and both sides of a connection need matching or compatible generated code.

What "schema evolution" actually protects you from

Systems change. A service that adds a new field, or a consumer that's running slightly older generated code than the producer, still need to interoperate without a coordinated simultaneous deploy. Protobuf's rule is straightforward: every field has a unique tag number, and as long as you never reuse a tag number for a different meaning, old code can safely skip fields it doesn't recognize, and new code sees a sensible default for fields the old producer never set. Renaming a field in the .proto source is harmless because only the tag number is on the wire, not the name — but reusing tag 3 for a field with a different meaning after it once meant something else is a well-documented way to silently corrupt data for any consumer still running old code.

Avro: the schema travels with the pipeline, not the message

Avro takes a third approach, common in data pipeline and streaming contexts like Kafka: individual messages carry almost no schema information themselves, because the schema is registered once, separately, in a schema registry, and messages just reference a schema ID. This makes Avro messages even more compact than protobuf for high-volume streaming, since you're not paying even the small tag overhead per field repeatedly, but it also means a consumer can't decode a message at all without independently fetching the matching schema from the registry first — the message is meaningless in isolation.

Picking a format for the situation in front of you

A public REST API consumed by many independent third parties benefits from JSON's readability and the fact that literally every language and tool understands it without any code generation step. Internal service-to-service calls inside one organization, especially high-volume ones where parsing cost and payload size actually show up in a latency or bandwidth budget, are where protobuf earns its added build-step complexity — this is a large part of why gRPC uses it by default. High-throughput event streaming through something like Kafka, where millions of small messages a day make even a few bytes of per-message overhead add up, is Avro's home turf, particularly when a central schema registry is already part of the architecture.

The debugging cost is real and worth planning for

Whatever binary format you pick, budget for the fact that nobody can eyeball a raw payload in production without tooling. Teams that adopt protobuf or Avro without also setting up an easy way to decode a captured message back to readable text during an incident tend to relearn, at 2am, why JSON's verbosity was buying them something.