Skip to content

vs Avro, Protobuf, SchemaPack

These three share shorn’s core idea — the schema is known out of band, so the payload need not describe itself.

CodecPersonNested event100 events
shorn8434,135
Avro / avsc8444,249
SchemaPack9444,235
Protobuf.js11575,684

shorn wins or ties on every fixture, but the difference from Avro and SchemaPack is only 0–3%. Size alone would not justify a new library. Protobuf is 30% larger on the nested event because it includes field tags. Those tags add overhead but enable schema evolution.

shorn now leads both directions on every fixture but Unicode. Avro was the last codec ahead of it on encode; generated record encoders and the framing work around them closed that.

FixtureOpshornAvroSchemaPack
Personenc25.16M17.10M12.55M
Persondec67.55M25.59M16.12M
Nested eventenc8.64M6.42M4.19M
Nested eventdec11.45M5.44M4.94M
100 eventsenc100.1K41.2K53.4K
100 eventsdec116.3K52.8K57.5K

Take the Person encode margin conservatively. Every codec in that table shares one process, and Avro’s Person encode read 21.06M, 20.72M and 21.69M before this change against 17.10M after it — with 20.74M when measured alone in its own process. Isolated single-codec runs on the same machine put shorn at 42.54 ns and Avro at 48.22 ns, a 13% lead rather than the 47% above. The other rows’ margins are wide enough that this does not reorder them. See Throughput.

With validation on both ends shorn leads both directions, though validation cost dominates each:

CodecBytesEncodeDecode
shorn + Zod88.93M12.07M
Zod + Avro88.47M10.10M
Zod + SchemaPack97.00M7.90M

There is no longer a measured encode gap to Avro. The comparison below is kept only because it still bounds what a hand-written codec can do; it predates the allocation improvements, the generated encoders and the framing work, and has not been rerun. Raw Person encoding now takes about 40 ns instead of 204.7 ns. The hand-written codec keeps every check performed by the interpreter and produces identical bytes:

InterpretedFair codegenWin
Person encode204.7 ns54.3 ns150.4 ns (73%)

Both directions now compile: an object schema with no optional fields builds its own record decoder and record encoder with new Function. That plus three fixes to the framing around the schema walk — the pooled-writer hand-off, finish(), and a one-pass Writer.string — is what took every encode fixture but Unicode past Avro. What is left on the write path is the Writer, whose per-leaf method call the generated source does not yet inline. Both paths fall back to the interpreter under a Content Security Policy that forbids new Function. See Throughput.

CodecWhat it requires
shornYou already use a Standard Schema validator
AvroA second schema model, kept in sync; Avro concepts leak into the API
ProtobufA .proto file, a compiler or reflection step, generated code, per-field tags. Also 187.75 µs cold and 25.93 KB gzip
SchemaPackA custom DSL, weak TS inference, a Node Buffer heritage needing a browser polyfill

shorn’s main difference is not size or speed. It is that you do not maintain a second schema.

CodecEvolution
AvroFull reader/writer resolution. Mature, cross-language
ProtobufField tags give append-only compatibility. Mature, cross-language
SchemaPackNone
shornMismatch detection only

Use Avro when you need automatic schema resolution. It ties shorn on size by keeping the writer’s schema out of band. Reproducing Avro’s evolution model is outside shorn’s scope. See Schema Evolution.

Avro and Protobuf have mature implementations in every language you need. shorn is TypeScript only — which is what lets it use JSON Schema instead of defining an IDL.

CodecMinifiedGzip
shorn17.57 KB5.39 KB
protobufjs/light88.35 KB25.93 KB

avsc needs a browser stream polyfill and SchemaPack a buffer polyfill, so neither has a clean browser number. Cold setup: shorn 52–66 µs, Avro 68.99 µs, Protobuf.js 187.75 µs, SchemaPack 3.00 µs.

UseWhen
shornTypeScript both ends, already validating, want no second schema and the smallest payload
AvroCross-language, or real schema evolution
ProtobufCross-language with an existing gRPC ecosystem
SchemaPackNode-only, raw speed, a custom DSL is acceptable

Generated formats — Bebop, FlatBuffers, Cap’n Proto — trade an IDL and compiler step for cross-language support and generated-code speed. Qualitative competitors until shorn has a specialized backend.