2026-09-02 · 8 min read
- json
- api
- data-integrity
JSON has one number production but applications have many numeric types. A producer may serialize a 64-bit integer exactly while a JavaScript client immediately stores it in a binary floating-point number that cannot represent all of its digits.
This matters for database identifiers, sequence numbers, financial minor units, high-resolution timestamps, and cryptographic metadata. A rounded value can still look plausible and remain valid JSON, making the defect harder to detect than a parse failure.
Classify values by meaning
Start with the domain rather than the source language's convenient type. Opaque identifiers are not quantities and usually do not need arithmetic. Decimal money, counters, timestamps, and scientific values each need separate precision and range decisions.
- Specify minimum and maximum values for quantities.
- Specify decimal scale and rounding for money.
- Treat identifiers as opaque even when they contain digits.
- State whether exponent notation and leading signs are accepted.
Choose a wire representation deliberately
A quoted decimal string is widely interoperable for exact integers, provided its grammar and canonical form are documented. A JSON number may be appropriate when every supported consumer can prove the required range is safe.
Schema formats such as int64 communicate intent but cannot force a generic parser to preserve digits. Generated clients, browser code, queues, observability pipelines, and test tooling all remain part of the contract.
Keep exact types through storage and computation
Parse exact values directly from the original string into an arbitrary-precision integer or the database driver's matching type. Do not pass through a floating-point number first.
Define comparison, sorting, and arithmetic behavior explicitly. Lexicographic sorting of unnormalized decimal strings is not numeric sorting, while converting them for convenience may recreate the original precision loss.
- Use canonical decimal strings when signatures or hashes cover values.
- Reject fractions where the domain requires integers.
- Bound digit length before expensive arbitrary-precision operations.
- Keep logs and metrics from coercing exact values into numbers.
Build cross-runtime contract tests
Use Flashman's JSON formatter to inspect wire values, number-base converter for exact fixtures, diff to expose digit changes, units converter for scale boundaries, and hash tool to identify canonical payload fixtures.
Test safe-integer boundaries, signed 64-bit boundaries, values beyond 64 bits, negative zero, exponent forms, quoted values, malformed digits, database round trips, generated clients, caches, logs, and every runtime in the supported path.