ToolzyLabToolzyLab

Code review guide · Reviewed and modified 2026-08-06

How to Compare Code Changes with a Diff Viewer

A diff shows what changed; the skill is knowing what matters. This guide covers reading diffs without drowning in noise — formatting storms, moved blocks, and generated churn.

Reading a diff: the vocabulary

A line-oriented diff treats one file as old and one as new: lines present only in the old version are removals, lines present only in the new are additions, and unchanged lines form the context that anchors each edit. The unified format marks removals and additions explicitly and surrounds them with context lines; the side-by-side view aligns the two versions horizontally. Both encode the same information — choose by the question, not habit: side-by-side excels at spotting what shifted within a line; unified excels at compactness and copy-paste.

The first reading skill is orientation: which version is left and which is right, because a swapped comparison reverses the apparent direction of every change. The second is scale: the count of changed hunks tells you whether this is a surgical edit or a rewrite, and the answer changes your review strategy — one hunk gets line-by-line scrutiny; fifty hunks get pattern recognition first. The third is the context window: enough surrounding lines to understand why the change sits where it does, which is where diffs either illuminate or mislead depending on how generously they show neighbors.

Separating signal from formatting noise

The most common diff failure mode is noise drowning signal: a two-line logic change buried in a reformat that touches every line. The defensive techniques are specific. Ignore-whitespace mode collapses pure indentation and spacing changes, exposing the content edits underneath — run it first on any diff that looks larger than expected. When formatting and logic travel together, ask whether they can be separated: a formatting-only version of the old file against the new isolates the real changes.

Generated files deserve their own treatment: build artifacts, lockfiles, and compiled output produce enormous diffs that carry no reviewable meaning. Recognizing a generated file's signature — machine-style names, thousands of mechanical changes, no human pattern — and skipping it deliberately is a review skill, not laziness. The discipline: review what humans wrote, acknowledge what machines generated, and never let a ten-thousand-line lockfile diff exhaust the attention budget a fifty-line logic change deserves. Noise management is not optional polish; it is the difference between reviews that find problems and reviews that rubber-stamp fatigue.

Moves, renames, and the illusion of rewriting

Line diffs have a blind spot for movement: a block relocated within a file appears as deletions plus additions, and a rename appears as a deletion and a creation. The visual result — massive change — contradicts the reality, which may be zero semantic difference. Modern diff tools detect moves and renames with similarity thresholds and report them as such; the review habit is asking the question whenever a diff looks disproportionate: did code move rather than change?

The verification move: compare the supposedly deleted and added blocks directly — a side-by-side of the two regions with whitespace ignored answers instantly whether they are identical twins. Renamed files deserve the same check: diff the old path against the new path, not against emptiness. The failure mode this prevents is real: a refactor that moved a hundred lines reviewed as if it rewrote them wastes attention; worse, a genuine rewrite disguised as a move escapes scrutiny. When the diff's shape suggests catastrophe, verify the substance before reacting — moves are cheap to confirm and expensive to misread.

Hunting problems: what reviews actually catch

The high-value review targets are consistent across codebases. Logic changes: condition flips, off-by-one shifts, altered defaults — the small edits with large blast radius. Removed code: what the deletion breaks, especially deleted validation, error handling, and fallback paths, because removals are invisible to tests that never exercised them. Added dependencies and imports: new surface, new failure modes, new trust decisions. Boundary behavior: the change handles the happy case — does it handle empty input, null, maximum values, concurrent access?

The reading technique that catches these: for each hunk, state in plain words what changed and what depends on it, then ask what could go wrong. The question 'why is this change shaped this way?' finds more than 'is this line correct?' — intent-level review catches design problems that line-level review cannot see. And the honest limit: diffs show changes, not absences — the fix that should have happened in a second file but did not is invisible in the first file's diff. Reviewing against the change's stated purpose, not just the change's content, is the discipline that catches the missing half.

Using the viewer: settings that change outcomes

Diff viewers carry settings that materially affect what you see, and knowing them converts the tool from display to instrument. Whitespace handling — ignore, or show — is the primary one, covered above. Context line count controls the neighborhood: wider context for understanding where changes sit, narrower for density when scanning many hunks. Character-level highlighting within changed lines pinpoints the exact modification in an otherwise-identical line — the difference between 'this line changed' and 'the operator flipped'.

The comparison modes deserve deliberate choice. Two-file comparison for the focused question — what did this edit do; directory comparison for the broad one — what did this branch touch across the project. The order of operations for reviewing a multi-file change: directory diff first for the landscape — which files, how much each — then file-by-file in order of risk, logic files before styles, hand-written before generated. The viewer does not review for you; it presents the evidence. The settings determine whether the evidence arrives legible or buried, and five seconds of configuration is the cheapest review-quality improvement available.

Verifying behavior: the diff is not the truth

The diff shows textual difference; behavior is what matters, and the two agree less reliably than reviewers assume. A change that looks harmless can alter timing, ordering, or edge behavior in ways no line-level reading predicts — the classic examples are whitespace-insensitive languages where the visible change understates the parse-level one, and configuration changes whose effects propagate through systems the diff never shows. The corrective is execution: run the tests, exercise the changed path, observe the behavior at the boundaries the diff implies.

The proportionality principle: verification effort scales with blast radius, not diff size. A one-line change to a payment calculation deserves more runtime verification than a hundred-line documentation rewrite. And the negative space deserves checking: the tests that pass are evidence of what works, not proof of what is safe — ask what tests would catch this change if it were wrong, and notice if the answer is none. The complete review is a triangle: the diff states what changed, the code states what it means, and the behavior states what is true. Reading all three is the craft; any one alone is a guess.

A reading technique for large diffs

Large diffs defeat linear reading — scrolling top to bottom through five hundred changed lines loses the plot within the first fifty — and the technique that works is structural rather than sequential. Start with the change map: which files changed, which grew, which shrank, which are new. File-level patterns tell the story before any line does: a change touching one module is a feature; one touching twenty files identically is a rename or dependency bump; one with massive churn in one file is a rewrite worth extra scrutiny.

Inside individual files, read removals before additions. Deleted code reveals what the author gave up — the intentions abandoned, the paths closed — and additions only make sense against that loss. Additions read alone describe what exists; removals explain why. Then trace the boundaries: where new code meets old, because integration seams are where regressions nest. A diff reviewed in this order — map first, deletions second, seams third — surfaces intent and risk that line-by-line reading never reaches.

The technique extends to what reviewers should verify beyond the diff itself: do the changes match the stated purpose, with no drive-by edits hiding in the middle? Are tests updated alongside behavior? Does anything change that the description did not mention? Each question is answerable from the structural read. The diff viewer is the instrument; the reading order is the skill. Code review quality is not about catching every line — it is about asking the right questions in an order that the diff can actually answer.

Frequently asked questions

What do the plus and minus signs mean in a diff?

Plus marks lines added in the new version; minus marks lines removed from the old. Unmarked lines are unchanged context.

Why does my diff show hundreds of changes from a small edit?

Usually formatting noise or moved code. Use ignore-whitespace mode, and check whether blocks moved rather than changed.

How do I review a renamed file?

Compare the old path against the new path directly. Diffing the new name against nothing shows a false complete rewrite.

Should I review generated files?

Recognize and skip them deliberately — lockfiles and build artifacts carry no reviewable meaning. Spend attention on human-written changes.

What matters most when reviewing a diff?

Logic changes, removals of validation or error handling, new dependencies, and boundary behavior — the small edits with large blast radius.

Does a passing diff review mean the change works?

No — diffs show textual differences, not behavior. Runtime verification proportional to the change's blast radius is the actual check.

Side-by-side or unified view?

Side-by-side for spotting in-line shifts; unified for compact scanning. Choose by the question you are asking.

Can a diff show changes that were never made?

Yes — the fix that should have accompanied a change but lives in another file is invisible in this file's diff. Review against the change's purpose.

How should I read a large code diff?

Start with the file-level change map, then read deletions before additions, then inspect the seams where new code meets old. Structure first, lines second.

What should I check besides the changed lines?

Whether changes match the stated purpose, whether tests moved with behavior, and whether any undocumented edits ride along inside the diff.