Skip to content
TwinScope0.3.10

The Diff Radar

Counts tell you how much changed. The radar tells you what kind of change it was — and, just as importantly, which questions this comparison could not answer.

Working with resultsEdit this page

Two comparisons of the same size can be nothing alike. Forty changed lines in one file and forty renamed files are both "40", and the number is the least interesting thing about either. The Diff Radar is the shape of a change: six axes, scored 0–100, from a Radar button beside the counts.

Click to enlarge
Six axes, two of them measured. A line diff knows about lines. The other four are hollow, and named underneath.

It is collapsed by default, because most comparisons are read by their numbers.

The rule that makes it worth trusting#

An axis an engine cannot measure is absent, never zero.

Zero is a claim. It says "nothing changed on this axis" — and a comparison of two images has nothing whatever to say about dependency licences. Reporting that as a zero would be a measurement nobody took, drawn as a measurement of nothing.

So the scores are a partial record, and three things follow from that:

  1. An absent axis is drawn hollow, at the centre, with a slightly larger empty ring than a real point, and the polygon is pulled in to meet it rather than pushed out to the rim.
  2. It is named in words, not only in the drawing — a line under the chart reads Not measured by this comparison: Visual, Metadata, Deps, Weight. A picture cannot be relied on to carry a negative.
  3. The legend only lists measured axes. There is no key to click for an axis with no score behind it.

The six axes#

AxisLabelWhat it counts
StructureStructureThings added or removed — keys, files, rows, packages
ContentContentThings changed in place, without appearing or disappearing
VisualVisualHow much of the rendered picture differs
MetadataMetadataRenames, type changes, licences — the facts around the data
DependenciesDepsHow much of the dependency set moved
PerformanceWeightWhether the change makes the thing bigger or heavier

Clicking an axis in the legend prints that sentence under the chart. The definitions are written down in the app rather than implied by the drawing, which is the point of the next two sections.

One curve, shared by every axis scored from a ratio#

Almost every score is "how much of this changed, out of how much there was", and they all go through the same function — a square root rather than a straight line. Linear is the wrong shape: two changed lines in a 4000-line file is 0.05%, which draws as nothing at all, and "a couple of lines changed" is exactly what you wanted to see.

Share of the whole that changedScore
1 in 10010
half71
all of it100

Nothing reaches the rim until the change really is total. And because it is one curve, a 62 on Structure in a folder comparison and a 62 on Content in a JSON one describe the same amount of movement.

Where an axis compares two quantities rather than counting a share — bytes, file sizes, package counts, pixel counts — the gap is measured against the larger of the two and then put through the same curve. So a thing that doubled and a thing that halved both score 71: both are large changes, in opposite directions.

Two definitions worth reading#

These are the two places the radar could have quietly lied, so both are worked out in the open.

Git's Content axis is scored on files, not lines#

Git will tell you how many lines changed. It will never tell you how many lines a file has — so there is no denominator for a line ratio, and inventing one is exactly what "only ship this if the scores are honest" rules out.

The first attempt scored a file's content against twice its own change count, which is arithmetic that always produces the same answer: 71, on every comparison, for every repository. A constant dressed as a measurement is worse than a gap, because it looks like data.

So a git comparison scores Structure, Content and Metadata on how many of the changed files were added or removed, modified, or renamed. That is a real ratio with a real denominator, and it is the same one the folder engine uses.

"Performance" had no definition, so it got one — and a new label#

The axis came from the product spec with a name and no meaning attached, which is precisely where a fabricated number gets in: any code can be written under a word nobody has defined.

It is defined here as weight — does this change make the thing bigger or heavier? — and relabelled Weight on the chart so the word matches the measurement:

ComparisonWhat weight means
FoldersTotal bytes in the tree
Binary filesFile size
DependenciesTransitive package count
ImagesPixel count
Saved pagesNumber of assets the page loads
PDFsNumber of pages

Everything else leaves it absent.

What each engine can measure#

Blind spots differ by engine, and that is the useful part: the shape of the gaps tells you what kind of comparison you are looking at.

EngineMeasuresCannot measure
Text and code, large textStructure, ContentVisual, Metadata, Deps, Weight
JSON, YAML, XMLStructure, Content, Metadata (type changes)Visual, Deps, Weight
CSVStructure, Content, Metadata (columns)Visual, Deps, Weight
DependenciesDeps, Structure, Content — plus Metadata and Weight from lockfilesVisual; and licences or transitive weight from a manifest pair
FoldersStructure, Content, Metadata (renames), WeightVisual, Deps
Git refsStructure, Content, Metadata (renames and copies)Visual, Deps, Weight
ImagesVisual, Structure, WeightContent, Metadata, Deps
BinaryContent, WeightStructure, Visual, Metadata, Deps
APIStructure, Content, MetadataVisual, Deps, Weight
ConfigStructure, Content, Metadata (secrets)Visual, Deps, Weight
Saved pagesStructure, Content, Metadata, WeightVisual — nothing was rendered
PDFStructure, Content, Metadata, WeightVisual — no page was rendered
Visual regressionVisual, Structure, ContentMetadata, Deps, Weight

Two of those rows are the rule earning its keep. The page and PDF engines both compare documents that have a rendered appearance, and neither of them renders one — so Visual stays blank rather than reporting a zero that would read as "it looks the same".

Why dependencies shipped first#

The Deps axis is the reason dependency comparison was built before the radar, out of order. Six axes where one of them can never be filled by anything is five axes and a decoration — and an axis that exists only to be hollow is not honest, it is unfinished. The data had to exist before the chart could claim to plot it.

A manifest pair still leaves two axes empty: package.json files resolve nothing, so there are no licences to compare and no transitive count to weigh. Compare the two lockfiles and both fill in. That difference is visible on the chart, which is the whole idea.

Every score comes from a number the engine already had#

There is no second pass over your files to produce a radar, and no metric that exists only for it. Each engine hands over counts it computed anyway — added, removed, modified, renames, changed cells, changed pixels, transitive packages — and the radar rescales them. Which means a score can never disagree with the counts in the summary strip above it: they are the same numbers, drawn twice.