The short version
- Line comparison suits code and config; word comparison suits prose; character comparison suits short strings and IDs.
- Ignore trailing whitespace by default. It hides nothing and it obscures a great deal.
- Reformat both sides before comparing structured data, or the diff is mostly formatting.
- A reordered JSON file with identical content still diffs — key order is text, even when it is semantically irrelevant.
- Invisible characters are the usual explanation for two files that look identical and do not match.
Comparing two versions of something sounds like it should have one answer, and it does not. A diff algorithm finds a way to transform the first input into the second, and there are usually many such ways — the tool picks a small one. That is why the same pair of files can produce a clean, readable diff or an unreadable wall of changes depending on settings that look cosmetic.
Two settings do almost all of the work. Granularity decides whether the unit of change is a line, a word or a character. Whitespace policy decides whether invisible differences count as differences at all. Get those two right for what you are actually comparing and the real change usually becomes obvious.
The third factor is format. Comparing JSON, CSV or code as though it were prose produces diffs dominated by formatting — a reindented file reads as though every line changed. Telling the tool what it is looking at lets it normalise the formatting first and compare the content.
Line, word or character
Granularity is the setting that changes the answer most, and each option is right for a different kind of comparison.
Line comparison treats a whole line as changed if anything in it differs. This is what version control shows you and it is the right default for code, configuration and any file where a line is a meaningful unit. Its weakness is prose: fix one typo in a long paragraph and the entire paragraph reads as rewritten, because a paragraph is one line.
Word comparison marks only the changed words within a line, which is what makes it correct for prose, contracts, copy review and translation. It is the setting that turns an unreadable paragraph-level diff into an obvious one-word change.
Character comparison goes finer still, and its useful range is narrow but real: short strings, identifiers, hashes, keys, URLs. When two supposedly identical values are being rejected as different, character comparison is what shows you the transposed digit or the extra character. On anything longer it produces noise.
| Granularity | Marks | Use it for |
|---|---|---|
| Line | The whole line, if anything differs | Code, config, logs, data files |
| Word | Only the words that changed | Prose, contracts, copy, translations |
| Character | Individual characters | IDs, hashes, keys, short strings, URLs |
Whitespace is why most diffs are unreadable
Whitespace differences are invisible on screen and count as differences to a comparison, which is the single most common reason a diff is far larger than the actual change. Three sources account for most of it.
Trailing whitespace at the end of a line is the most frequent and the most pointless — many editors add or strip it automatically, so it appears in diffs of files nobody meaningfully edited. Ignoring trailing whitespace should be the default; it genuinely hides nothing.
Line endings are the classic cross-platform trap. Windows ends lines with a carriage return and a newline, Unix and macOS with a newline alone, so a file that has been round-tripped between systems can report every single line as changed while looking untouched. This is what a whitespace-insensitive comparison exists for.
Indentation changes are the third, and here you have to think. A file converted from tabs to spaces, or reindented by a formatter, differs on almost every line without changing behaviour — so ignoring whitespace entirely shows you the real change. But in a whitespace-significant language such as Python or YAML, indentation *is* semantics, and ignoring it can hide a genuine bug. Ignore whitespace to find the change, then look again with it on.
- Ignore trailing whitespace by default — it hides nothing.
- Ignore all whitespace when comparing across platforms or after a reformat.
- Be careful ignoring whitespace in Python or YAML, where indentation carries meaning.
- If two files look identical but do not match, suspect line endings first.
Comparing structured data
JSON, XML, CSV and SQL all have the same problem: their textual form carries information their content does not. Two API responses can be semantically identical and textually completely different because one is minified and one is pretty-printed.
The fix is to normalise both sides before comparing — reformat them to a consistent shape so the diff reflects content rather than layout. This turns comparing a minified response against a formatted one from useless to trivial, and it is the single most valuable thing to do before comparing structured data.
Key order is the subtlety that remains. Reformatting fixes indentation and line breaks but does not reorder keys, so a JSON object with the same pairs in a different sequence still diffs. That is honest rather than a limitation: the two files really are different text, and whether the difference matters depends on the consumer. Most JSON parsers treat object key order as insignificant; plenty of snapshot tests and signature schemes do not.
For CSV the equivalent trap is column order and quoting style. A file that has been through a spreadsheet often comes back with different quoting, a changed line ending and sometimes a byte-order mark at the start, none of which changes the data.
When two identical-looking files will not match
Occasionally two files look the same at every zoom level and still compare as different. The cause is almost always a character that does not render distinctly, and there is a short list of usual suspects.
A byte-order mark is an invisible marker some editors and spreadsheet exports write at the very start of a file. It makes the first line differ while looking identical, and it breaks a surprising amount of parsing.
Non-breaking spaces are the next most common, and they arrive by copy and paste from web pages, word processors and design tools. They occupy the same visual space as an ordinary space and are a different character entirely.
Then there are the lookalikes: curly quotes substituted for straight ones by autocorrect, an en dash where a hyphen was typed, and — in text that has passed through multiple systems — Cyrillic or Greek letters that are visually identical to Latin ones. Character-level comparison on the specific line is how you find any of these.
Reading a diff without misinterpreting it
A diff shows an explanation, not a history. If a block of text moved from the top of a file to the bottom, most diffs report it as deleted in one place and added in another, because that is a valid and small way to describe the transformation. Nothing recorded that it moved.
This matters when reviewing a large refactor. A change that reads as hundreds of deletions and hundreds of additions may be a pure move with no edit at all, and reading it as rewritten work is a genuine misjudgement. Comparing the moved block against itself separately is the way to confirm.
The related caution is that a small diff is not a safe diff. Changing a comparison operator or a single digit in a limit is one character and can be the most consequential change in a release. Diff size measures textual distance, never risk.
Try it on Diff Checker
Free, no signup, no watermark. Runs on Zyff’s servers, so you get the same result on a phone as on a desktop.
At a glance
| Formats recognised | Plain text, JSON, HTML, XML, CSS, JavaScript/TypeScript, YAML, CSV, SQL, Markdown, other code |
|---|---|
| Granularity | Line, word or character |
| Whitespace policies | None, trailing, leading and trailing, or all |
| Reformat before comparing | JSON, HTML, XML, CSS, JavaScript, SQL, CSV |
| Comment stripping | JSON, HTML, XML, CSS, JavaScript, YAML, SQL |
| Price | Free, no account needed |
Frequently asked questions
How do I compare two files and see the differences?
Paste both versions into Zyff's diff checker, set the format so it knows what it is reading, and choose a granularity — line for code and config, word for prose. Turn on trailing-whitespace tolerance, which removes a large class of invisible differences that make diffs unreadable without hiding anything real.
What is the difference between line, word and character comparison?
Line marks a whole line as changed if anything in it differs, which suits code and config. Word marks only the changed words, which is what makes prose diffs readable — a one-word fix in a long paragraph shows as one word rather than a rewritten paragraph. Character goes finer and is for short strings, IDs and hashes.
Why does my diff show every line as changed?
Usually line endings. Windows and Unix terminate lines differently, so a file that has moved between systems can report every line as modified while looking untouched. The other common causes are a reformat that changed indentation and a tabs-to-spaces conversion. Comparing with whitespace ignored confirms it immediately.
How do I compare two JSON files properly?
Set the format to JSON and reformat both sides before comparing, so indentation and line breaks are consistent and the diff reflects content. Note that reformatting does not reorder keys — an object with the same pairs in a different order still diffs, which is accurate, since whether that matters depends on what consumes the file.
Two files look identical but do not match. Why?
An invisible character, almost always. The usual suspects are a byte-order mark at the start of the file, a non-breaking space picked up by copy and paste, or a curly quote substituted for a straight one by autocorrect. Switch to character comparison on the offending line and it becomes visible.
Should I ignore whitespace when comparing files?
Ignore trailing whitespace always — it hides nothing. Ignore all whitespace when comparing across platforms or after a formatter has run. Be careful in Python or YAML, where indentation is meaningful and ignoring it can conceal a real bug: find the change with whitespace ignored, then re-check with it on.
Why does moved text show as deleted and added?
Because a diff describes how to transform one input into the other, and deleting a block in one place and adding it in another is a valid, small description. Nothing records that it moved. This matters when reviewing refactors — a change that looks like hundreds of rewritten lines may be a pure move with no edit at all.