Skip to main content
Back to BlogCode Guides

How to Use a Diff Checker (and Why 'Modified' Lines Are Two Lines, Not One)

Diff checkers use the Longest Common Subsequence algorithm, which has no modify operation — only equal, add, and remove. Learn how LCS works (dynamic programming, O(m times n), greedy fallback for large inputs), why what the UI calls a modified line is actually a removed line paired with an added line by Levenshtein similarity, when to use line vs word vs character granularity, and how ignore options normalize before comparing.

The Toolbox TeamAugust 13, 20268 min read

The problem: "modified" is a lie the diff UI tells you

A diff checker compares two texts and shows what changed. The output looks simple — green lines were added, red lines were removed, and some lines are shown as "modified" with inline highlights. But underneath, there is no "modify" operation. The algorithm has exactly two operations: equal and not equal. A line that changed is not one line that was edited. It is two separate lines: one removed from the original, one added to the new version. The tool pairs them together and calls the pair "modified" because showing every change as a delete-then-add is unreadable for humans. Understanding this distinction is the difference between reading a diff and understanding what the diff actually tells you.

The tool uses the Longest Common Subsequence (LCS) algorithm, the same family of algorithm that powers git diff. It works on any text — code, prose, JSON, configuration files — and compares at three granularity levels: lines, words, or characters.

Fastest path

Open the Diff Checker, paste the original text in the left panel and the changed text in the right panel, pick a comparison mode (lines, words, or characters), and click Compare. The tool shows a side-by-side split view with additions in green, removals in red, and modified lines with inline word-level highlights. Switch to the Unified tab for a single-panel view. Use the Ignore options to strip whitespace, case, blank lines, or comments before comparing.

The LCS algorithm and why diff output looks the way it does

The Longest Common Subsequence is not the same as the longest common substring. A substring must be contiguous. A subsequence just needs to be in order. "A something B something C" has the subsequence "ABC" even though the letters are not adjacent. This distinction matters because it means the diff algorithm can find unchanged content even when lines were inserted or deleted in the middle of a matching region.

The algorithm builds a dynamic programming table of size m times n, where m and n are the number of tokens in each input. Each cell stores the length of the LCS up to that point. After filling the table, it walks backward from the bottom-right corner to reconstruct the actual sequence. The time complexity is O(m times n), which is fine for typical files but becomes slow for inputs with hundreds of thousands of tokens. The tool falls back to a greedy heuristic for inputs that would exceed 10 million table cells — faster but less optimal, meaning it might not find the absolute longest common subsequence, just a reasonably long one.

The output is a sequence of operations: equal (unchanged), added (in the new text only), removed (in the original only). The tool renders this as colored lines. Equal lines are shown in both panels. Added lines appear only in the right panel. Removed lines appear only in the left panel. There is no "modified" operation in the algorithm.

How "modified" lines work: pairing by similarity

When you see a line marked as modified with inline word-level highlights, the tool did two things. First, the LCS algorithm produced a removed line followed by an added line. Second, the tool computed a similarity score between the removed and added lines using Levenshtein distance — the minimum number of single-character edits (insert, delete, substitute) needed to transform one into the other. If the similarity exceeds a threshold (15% in the tool), the pair is displayed as a single "modified" line with inline highlights showing which words changed, rather than as separate removal and addition.

This pairing is a UI convenience, not part of the diff algorithm. The same two lines could be shown as "removed line A, added line B" or as "modified line A to B" depending on whether the similarity threshold is met. A line that changed from const x = 5 to const x = 10 will pair as modified (high similarity). A line that changed from const x = 5 to function calculateTotal() will not pair (low similarity) and will appear as separate removal and addition.

Three granularity levels

The tool compares at three levels: lines, words, or characters. Line-level diff is the default and the most common use case — it splits both inputs on newlines and runs LCS on the resulting arrays. Line-level diff is fast and readable for code and structured text.

Word-level diff splits on whitespace and punctuation boundaries. It is useful when changes happen within a single line and you need to see which specific words changed — useful for prose, legal documents, or configuration values where a single word change matters.

Character-level diff splits into individual characters. It catches every change including single-character edits (a typo fix, a punctuation change). The output is harder to read because every character is a token, but it is the most precise comparison available. Character-level diff on large inputs is expensive — the m times n table grows quickly, and the greedy fallback may kick in.

The inline diff feature adds a second layer: within a paired "modified" line, the tool runs a word-level LCS to highlight the specific words that changed. This is separate from the main comparison granularity — you can use line-level diff for the main comparison and still see word-level inline highlights on modified lines.

Split view vs unified view

Split view shows the two texts side by side with removed lines on the left and added lines on the right. It is easier to read for code review because you can see the before and after in context. Unified view shows a single stream with minus prefixes for removed lines and plus prefixes for added lines — the format used by git diff and the unified diff standard. Unified view is more compact and is the format used in patches.

Ignore options: normalization before comparison

The Ignore options (whitespace, case, blank lines, comments, trailing whitespace) do not filter the output. They normalize the inputs before the LCS algorithm runs. "Ignore whitespace" strips all whitespace from both inputs before comparison, so a line that changed only in indentation shows as unchanged. "Ignore case" lowercases both inputs, so "Hello" and "hello" show as equal. The diff is computed on the normalized text, but the display shows the original text — so you see the real lines, just without the changes you chose to ignore.

Gotchas

  • LCS does not detect moved blocks. If you cut a paragraph from line 10 and paste it at line 50, the algorithm sees it as a removal at line 10 and an addition at line 50 — not a move. Git has the same limitation. The tool does not detect moves, only insertions and deletions.
  • Character-level diff on large inputs is slow. The DP table is m times n characters. A 10,000-character input pair produces a 100-million-cell table. The tool falls back to greedy mode above 10 million cells, which is faster but may miss some matches. Use word-level or line-level diff for large inputs.
  • "Ignore comments" covers four syntaxes. The tool removes //, #, /* */, and <!-- --> comments before comparison. If your code uses a different comment syntax (Lua's --, SQL's --), those lines are not stripped and will show as changed even if only the comment changed.
  • Syntax highlighting is decorative, not semantic. The tool auto-detects the language and applies syntax highlighting to the diff output. The highlighting does not affect the comparison — a keyword highlighted in one panel and not the other still compares as equal if the text matches.
  • The unified diff export includes file headers. The exported unified diff includes --- and +++ header lines and @@ hunk markers. These are part of the unified diff format standard and are required for patch and git apply to work. If you are pasting the diff into a code review tool that expects raw changes, strip the headers or use the HTML export instead.

Summary

  • A diff checker uses the Longest Common Subsequence (LCS) algorithm to find the longest sequence of tokens that appear in both inputs in the same order. Everything not in the LCS is an addition or removal. There is no "modify" operation — modified lines are a UI pairing of a removed line and an added line with high Levenshtein similarity.
  • Three granularity levels: lines for code and structured text, words for prose and inline changes, characters for maximum precision on small inputs. The inline diff feature adds word-level highlights within modified lines regardless of the main granularity.
  • Split view for readability, unified view for patches and compact output. Ignore options normalize inputs before comparison — they do not filter the output.
  • Use the Diff Checker for the comparison, the Regex Tester for pattern matching within changes, the JSON Formatter for normalizing JSON before diffing, and the Text Diff Merger for text-focused comparison.