ADR-002 — JSON as the journal event format
JSON parses 10,000 events in 6 ms. YAML takes 496 ms for the same data.
Date: 2026-09-02 · Status: accepted · Scope: architecture
Context
A spike showed that reading 10,000 events took 0.43 s — twice the 200 ms guardrail. The events were stored as YAML.
The question was what actually caused it: the number of files, the filesystem, or the parser. Measured on the same 10,000 events:
| Format | Parsing 10k events | Bytes per event |
|---|---|---|
JSON (JSON.parse) | 6 ms | 174 |
YAML (yaml 2.9.0) | 496 ms | 150 |
A difference of 83×. Almost all of the time measured in the spike was the YAML parser, not I/O.
Options considered
| Option | For | Against |
|---|---|---|
| JSON | Native parser, 83× faster; zero dependencies; JSON.parse executes no code | 24 more bytes per event; quoted keys read slightly worse |
| YAML | Friendlier to a human; smaller | 496 ms on 10k events exceeds the whole CLI budget by itself; pulls a 686 KB dependency |
| JSONL (one line per event) | Compact and fast | Returns to a shared file, and that was 2 of 3 conflicts in the spike — it contradicts the product's main thesis |
| MessagePack and other binary formats | Fastest and smallest | Kills git diff as a human-readable journal, which is the product's main advantage |
Decision
JSON, one object per file, no indentation (JSON.stringify without space).
The readability argument that usually favours YAML barely applies here: an event
has seven fields and reaches the user through kadence log, not by being read
off disk by eye. Where readability genuinely matters — task descriptions — the
format stays Markdown with frontmatter.
Consequences
- An 83× parsing speedup for zero lines of code, one dependency fewer, and a
git diffthat shows a change as a whole event rather than a set of lines. - The journal is 14% larger, and
caton an event file is less pleasant than it could be. - What would make us revisit it: if events ever grow into nested structures with dozens of fields, single-line JSON becomes unreadable in a diff — and the answer then is indented JSON, not YAML.