The step even Protobuf can't skip
JSON has to be parsed from text. Protobuf and the binary formats are far more compact, but they still have a deserialization step: reading the wire bytes and building in-memory objects before you can touch a field. For most apps that's fine. But in games, high-frequency trading, and IPC where you read millions of messages a second — or a huge file you want to mmap and touch a few fields of — that unpack step is the bottleneck. FlatBuffers and Cap'n Proto remove it entirely with zero-copy access.
What "zero-copy" actually means
The trick is that the serialized layout is already the in-memory layout. Instead of parsing bytes into a new object graph, these formats lay data out so that a field lives at a known offset, and you read it directly from the raw buffer:
Protobuf: [ wire bytes ] ──parse──▶ [ in-memory objects ] ──▶ read field
FlatBuffers:[ buffer ] ─────────────────────────────────────▶ read field @ offset
(no parse, no allocation)You receive a buffer over the network or mmap it from disk, and accessing monster.hp just follows an offset into those exact bytes. There is no decode, no allocation, no object construction — which is why Cap'n Proto's tagline is "infinity times faster" than parsing: the parse step is zero work because there is no parse step.
FlatBuffers vs Cap'n Proto
Both are zero-copy and schema-driven (you define types in an IDL and generate accessors), with a similar niche:
- FlatBuffers — from Google, born for game development and mobile. Extremely mature, tiny runtime, broad language support. You access data through generated accessor methods that read directly from the buffer. Optional verification checks a buffer is well-formed before you trust it.
- Cap'n Proto — by a Protobuf co-author, designed as "Protobuf done for zero-copy." Same zero-copy core plus a built-in RPC system with *promise pipelining* (chaining dependent calls without extra round-trips). Slightly richer feature set aimed at inter-service communication.
Both trade some size and flexibility for that read speed: the buffers are larger than tightly-packed Protobuf (fields are aligned to fixed offsets), and the rigid layout is less forgiving to hand-edit.
The trade-offs — and why JSON usually still wins
Zero-copy is a specialist tool, not a default. Weigh it honestly:
- You need a schema and codegen. Like Protobuf, there's no self-describing, human-readable payload — you can't just
curlit and read it. - Bigger than Protobuf on the wire. Fixed offsets and alignment cost bytes; if you're bandwidth-bound rather than CPU-bound, Protobuf may serialize smaller.
- Rigid and low-level. The ergonomics are more awkward than JSON or even Protobuf — you work through generated accessors, not plain objects.
- Overkill for most apps. If you're not reading millions of messages per second or memory-mapping large files, the parse step you're eliminating wasn't your bottleneck.
For nearly every web API, config file, and inter-service message, JSON remains the right choice for its universality and readability, and Protobuf covers most cases where you've outgrown JSON's size and speed. Zero-copy formats earn their place only at the extreme end.
When to reach for zero-copy
- Game engines — deserializing entity and asset data every frame; FlatBuffers is a staple here.
- Ultra-low-latency systems — trading, real-time telemetry, anything counting microseconds per message.
- Memory-mapped large files — touch a few fields of a huge dataset without loading or parsing the whole thing.
- High-throughput IPC / RPC — millions of small messages between processes (Cap'n Proto's RPC shines).
The decision ladder is: JSON for interoperability → **Protobuf** when size and speed matter → FlatBuffers/Cap'n Proto when even the deserialization step is too expensive. For the analytics-shaped version of "compute without parsing," see Apache Arrow vs JSON. To measure whether you actually have a payload problem first, check your JSON size.