Skip to content

Why shorn?

Many formats describe the same payload twice: once in the schema and again through field names and type tags in the bytes. Self-describing formats need that information because they can decode without a schema. If both endpoints already use the same validation schema, shorn can leave it out of the payload.

// JSON: field names travel with every record.
const bytes = new TextEncoder().encode(JSON.stringify(person)); // 35 bytes
// shorn: field names stay in the schema.
const bytes = encode(Person, person); // 8 bytes

Compared with JSON converted to and from a Uint8Array, shorn is 77% smaller for this record and faster in both directions on every measured fixture:

Fixtureshorn encJSON encshorn decJSON dec
Person25.16M4.74M67.55M4.67M
Unicode person7.74M3.77M7.68M3.58M
Nested event8.64M1.40M11.45M1.74M
100-event batch100.1k36.3k116.3k21.2k
Person, validated8.93M3.69M12.07M3.54M

shorn is 2.1–6.2× faster to encode and 2.1–14.5× faster to decode. It now wins all ten comparisons against JSON.stringify, even though that baseline stops at a string instead of producing bytes. Sending the string over a socket still requires a byte conversion, whether your code or the platform performs it.

The formats that beat shorn on speed require a different tradeoff:

CodecWhat it requires
AvroA second schema model, kept in sync with your validator
ProtobufA .proto file, a compiler step, and per-field tags on every record
SchemaPackA custom DSL, weak TS inference, Node Buffer heritage
msgpackr recordsOut-of-band record state both ends must synchronize
MessagePack / CBORNo schema, but payloads are about 3× larger in these fixtures

shorn’s ask is that you already use a Standard Schema validator.

AxisResult
Raw sizeBest or tied in every fixture
Gzip sizeBest in both 100,000-event profiles
Brotli sizeSixth on repetitive data, second on high-entropy
Bundle5.39 KB gzip, smallest measured; 9% margin over /msgpack
SpeedFastest measured in both directions on every fixture but Unicode
Cold setup52–66 µs including Zod schema construction
EvolutionNone. Detection only
Cross-languageTypeScript only

The supported claim is: small TypeScript-native payloads from the validator you already use, with no schema language, generator, or build step. shorn is not the smallest after every compressor; Brotli can make msgpackr records or Protobuf smaller. It leads the measured field in both directions now, but only on these fixtures, on one machine, against these versions.

  • Cross-language readers → Avro or Protobuf.
  • Evolution with resolution, not detection → Avro.
  • Streaming or random access → shorn encodes whole values.
  • You are not validating → shorn has no schema to use; choose a self-describing format such as MessagePack.
  • Both ends pinned and size is not a cost → JSON is universal and inspectable.