Not a rivalry β a boundary decision. JSON wins where humans and openness matter; Protobuf wins where the same message flies millions of times between machines that already share a schema.
{"name":"Bob","id":150,"admin":true}
35 bytes as JSON. The same message in Protobuf:
0A 03 42 6F 62 10 96 01 18 01 β 10 bytes
"name", "id", "admin").
Protobuf replaces each name with a one-byte numeric tag and stores 150 as two bytes instead
of the three ASCII characters 1 5 0.
| Dimension | JSON | Protobuf |
|---|---|---|
| Payload size | Large (names repeat) | Small (numeric tags, varints) |
| Encode / decode speed | Slower (text scan, stringβnum) | Faster (byte-oriented) |
| Human-readable | β Yes | β Needs a decoder |
| Schema required | No | Yes (.proto) |
| Self-describing | β Field names inline | Only numeric tags |
| Cross-language types | Loose (numbers, strings) | Strong, generated |
| Schema evolution | Ad-hoc / by convention | Built-in, rule-based |
| Browser-native | β
JSON.parse | Needs a library |
| Tooling / grep-ability | Universal | Requires schema + tools |
150 arrives as bytes, not as the characters
"150" that must be converted.protoc --decode, and friction at the
browser edge. On tiny, infrequent messages the size win is negligible and the operational cost
is real. And note: neither format compresses β a gzip layer narrows the size gap for
text-heavy payloads.
.proto definitions. Next, see how Protobuf stacks against
Avro, MessagePack, and FlatBuffers.