How a diff engine decides what changed
A diff is not a character-by-character subtraction. The engine looks for the Longest Common Subsequence (LCS) — the biggest set of lines shared by both files in the same order — and treats those as anchors. Everything between the anchors is either something the old file had and the new one dropped (a deletion) or something the new file gained (an addition). The diagram below shows the alignment: matching lines stay put, and only the genuinely-changed lines get colored.
Because a change can be described in more than one valid way, two diff tools can color the same edit differently and both be correct — after you apply either result the files are identical. That freedom is exactly what the different algorithms exploit.
Diff algorithms compared
Git, GitHub, and most modern tools default to Myers' algorithm (Eugene Myers, 1986 — "An O(ND) Difference Algorithm and Its Variations"), but you can switch. The trade-off is between finding the mathematically shortest edit and finding the most readable one for a human reviewer.
| Algorithm | What it optimizes for | Best for | Weakness |
|---|---|---|---|
| Myers (default) | Shortest edit script (fewest inserts/deletes) | General-purpose, fast on almost anything | Can align on repeated/blank lines and split changes oddly |
| Minimal | Absolute smallest diff, even if slow | Tiny files where you want the theoretical minimum | Slower; rarely worth it in practice |
| Patience | Anchoring on lines unique to each side | Refactors, moved blocks, source code review | Slightly slower than Myers |
| Histogram | Patience's idea, tuned for speed and quality | The best default for reviewing code changes | Marginally more complex; still fast |
| Which should I use? | — | Reviewing code: try histogram. Everything else: the myers default is fine. | — |
Switch algorithms in Git with git diff --diff-algorithm=histogram, or set it permanently with git config --global diff.algorithm histogram.
Line-level vs word-level: a decision matrix
The single biggest quality lever in a diff is granularity. Line-level highlights whole lines; word-level highlights only the changed tokens inside a line. Neither is "better" — they suit different content.
| Your content | Use this granularity | Why |
|---|---|---|
| Source code, config files (JSON, YAML, TOML) | Line-level | Line boundaries are meaningful; a changed line usually means a changed statement |
| Prose, articles, documentation | Word-level | A one-word edit shouldn't repaint the whole paragraph |
| Legal contracts, policy documents | Word-level | You need to see the exact clause that moved, not the whole section |
| Long single-line strings (minified JS, URLs, CSV rows) | Character-level | There are no line breaks to anchor on |
| Reformatted code (reindented, tabs→spaces) | Line-level + ignore whitespace | Collapses cosmetic noise so real logic changes surface |
| Data files where key order varies (JSON/YAML) | Normalize first, then line-level | Sort keys and pretty-print both sides so equivalent data doesn't read as different |
Reading unified diff format
When you copy a diff out of Git or diff -u, you get unified diff format — the same thing patch tools and code review platforms consume. Each change block is a "hunk" with a header telling you where it lands:
@@ -12,7 +12,8 @@ function start(port) {
const app = express();
- let cache = null;
app.listen(port);
+ log('ready');
+ metrics.mark('boot');
return app;
The header @@ -12,7 +12,8 @@ means: in the old file the hunk starts at line 12 and spans 7 lines; in the new file it starts at line 12 and spans 8 lines. Lines with - were removed, lines with + were added, and unprefixed lines are unchanged context (usually three lines each side) that helps patch locate the hunk even if surrounding line numbers drifted. This is why a unified diff can be applied to a slightly different version of a file than the one it was generated from.
Common use cases
- Code review — Compare two versions of a source file to see what a change actually touched before you merge it. Ignore whitespace to strip out reformatting noise, and reach for a histogram diff on gnarly refactors.
- Document and contract comparison — Track revisions between drafts of an agreement, article, or spec. Word-level highlighting shows the exact clause that changed.
- Configuration drift — Diff a known-good config against what's running in production to spot the one changed value that broke a deployment.
- Debugging "identical" outputs — When two runs "should" produce the same output but don't, a diff pinpoints the byte that differs — a trailing newline, a locale-formatted number, a reordered key.
- Verifying copied content — Compare an original against a suspected duplicate to see precisely which passages match and which were altered.
Related developer tools
- JSON Formatter → — Pretty-print and sort JSON keys so two objects diff cleanly.
- Base64 Encoder/Decoder → — Decode payloads before comparing them as readable text.
- View all developer tools → — The full free suite of developer and design utilities.
Need expert code review?
Our development team provides professional code reviews, security audits, and quality assurance for your applications.