case study

A Config Diff of 128 Lines Hid Two Real Changes, and Canon Named Both in Two Lines

Here’s a familiar review. The config for a checkout service comes back from someone’s editor and the diff is a wall of red and green. The editor saved with four spaces instead of two, sorted the keys, and escaped the “ü” in the description. Somewhere in there, maybe, a value changed.

We built that situation on purpose. One service config of 66 lines covers the server, database, cache, limits, features, logging and deploy settings. The deploy tool writes it one way. An editor saves it another way and changes two values on the way: the database pool timeout drops from 30000 ms to 3000 ms, and the replica count turns from the integer 3 into the float 3.0.

A text diff of two config copies shows 128 changed lines, sorting and re-indenting cuts it to 4 lines without paths, and bxm-canon diff shows two lines with full paths

Plain diff -u reported 128 changed lines. Every line of the file moved or changed indent, so the real change is in there with everything else.

The usual fix is to normalise both files first, with sorted keys and one indent, and diff again. That got it down to 4 lines:

-      "timeout_ms": 30000
+      "timeout_ms": 3000
-    "replicas": 3
+    "replicas": 3.0

Better, but it took a Python one-liner on both files first, and the lines come without their place in the file. Which block does that timeout_ms belong to? Here you’d find it quickly. In a config where several blocks use the same key names, you’d go and look it up.

bxm-canon diff --text deployed.json edited.json printed two lines:

~ $.database.pool.timeout_ms 30000 -> 3000
~ $.deploy.replicas 3 -> 3.0

It compares values, not text, so indentation, key order and escapes don’t show up at all. Each change comes with its full path. The exit code is 1 when anything differs, so a CI step can fail on it.

Is 3.0 a real change?

Canon says yes: 3 is an int and 3.0 is a float, and they’re different values. A lot of tools would read both as the number three. Some won’t. A Go service decoding into an int field refuses 3.0, and so does a strict schema. If a tool in your pipeline turns ints into floats, you’d rather hear about it in review than in a failed deploy. If that difference really doesn’t matter for a file, the line is easy to ignore, because it’s labelled.

Run it yourself

The files and the script are in examples/case-studies/02-config-diff in the repository, and the recorded output is test/results/case-studies/02-config-diff.txt. The command is on the download page, and every flag is on the reference page.

More examples are on the use cases page.