Structural comparison beats text comparison
Comparing two JSON documents as text produces misleading results, because JSON's meaning does not depend on its formatting. Two documents differing only in indentation, or in the order of keys within an object, are semantically identical — but a text diff reports every line as changed.
A structural diff parses both documents and compares the resulting values, so formatting and key order are correctly ignored. What it reports is what actually differs: keys added, keys removed, and values changed.
The asymmetry to remember is that object key order is insignificant while array order is significant. {"a":1,"b":2} equals {"b":2,"a":1}, but [1,2] does not equal [2,1]. A tool that ignored array order would hide real differences, so arrays are compared positionally.
Types, numbers and null
JSON distinguishes types, and a structural diff respects that. The string "1" and the number 1 are different values, which is a difference worth surfacing — it frequently indicates a serialisation change on one side, such as an API that began quoting numeric identifiers to avoid precision loss.
Numbers have no defined precision in JSON itself, and most parsers use IEEE 754 doubles. So 1, 1.0 and 1e0 all parse to the same value and compare equal. Large integers beyond 2⁵³ may lose precision on parse, meaning two documents with genuinely different identifiers can compare equal after round-tripping — a real hazard when comparing payloads containing 64-bit IDs.
The three ways of expressing absence are distinct and worth distinguishing: a key present with value null, a key absent entirely, and a key present with an empty string. APIs frequently treat these differently — null often meaning "clear this field" and absence meaning "leave unchanged" — so a diff that conflated them would hide the most consequential change of all.
Array comparison and its limits
Positional array comparison has an obvious weakness: inserting one element at the start shifts everything, so a diff reports every position as changed even though only one item was added. This is the array equivalent of the line-ending problem in text diffs, and it makes output disproportionately noisy for the smallest possible edit.
Where array elements have a stable identifier — an id field, typically — matching by that key rather than by position produces far more useful output, correctly reporting one insertion. This is what most API-oriented diff tools do when configured with a key.
The general lesson is that a diff is only as good as its model of what the data means. For configuration files, comparing sorted arrays may be right if order is not significant; for an ordered list of steps, it definitely is not. To validate the documents before comparing, the JSON formatter reports syntax errors with their exact position.