A formatter and a linter get lumped together because they both run against your source code before it ships, but they do fundamentally different jobs. A formatter rewrites layout — indentation, line length, quote style, where a line breaks — without changing what the code does. A linter reads the code for risk: unused variables, unreachable branches, patterns that are technically legal but likely to be a bug. Confusing the two, or running only one, leaves a real gap in what your tooling catches.
What changed in 2026
- Combined tools became the practical default for new projects. Biome for JavaScript and TypeScript, and Ruff for Python, run formatting and linting from a single fast binary, which removes the two-tool, two-config setup that used to be standard.
- Formatter-linter conflicts got easier to avoid. Config presets that disable a linter's stylistic rules — the ones that would otherwise fight with a formatter's opinions — ship by default in most modern setups instead of requiring manual configuration.
- Format-on-save became close to universal, while linting increasingly runs as a CI gate rather than purely an editor nicety, splitting the two tools' primary moment of enforcement.
- Speed stopped being a reason to skip either tool. Rust- and Go-based implementations made full-project formatting and linting fast enough to run on every save and every commit without noticeable delay.
What a formatter actually checks
A formatter has no opinion about whether your code is correct. It only cares about layout: two spaces or four, single quotes or double, where a long line should wrap, whether there is a trailing comma. Feed it any syntactically valid code and it rewrites the layout to match one consistent style — it cannot introduce a bug, because it never touches logic, only whitespace and punctuation.
What a linter actually checks
A linter parses your code into a structural tree and walks it looking for patterns: a variable declared and never used, a comparison that always evaluates the same way, an import that does not exist. Some of what it flags is stylistic too, such as naming conventions, but its core job is risk detection, not layout. A linter can be wrong, since a flagged pattern might be intentional, but it can also catch a real bug before it ships, which a formatter never will.
Formatter versus linter, side by side
|
Formatter |
Linter |
| Job |
Rewrites layout and whitespace |
Flags risky or incorrect patterns |
| Can it change behavior |
No, by design |
Rarely, only via opt-in auto-fixes |
| Typical trigger |
On save, on commit |
On save, in CI, on commit |
| Common tools (JS/TS) |
Prettier, Biome |
ESLint, Biome |
| Common tools (Python) |
Ruff format, Black |
Ruff check, Pylint |
| Config disagreements |
Rare — one style, one config |
Common — teams argue over which rules to enable |
Why you need both, not either
A formatter alone gives you consistent-looking code with zero guarantee it works correctly. A linter alone gives you a list of real risks buried in code that might still be inconsistently spaced and hard to scan, because nothing is enforcing layout. The two solve different problems, and most real project setups run both, the formatter for consistency and the linter for correctness, as part of the same build pipeline or pre-commit step.
Making the two work together
The most common failure mode is a linter's stylistic rules disagreeing with the formatter's output, where the linter flags a line the formatter just wrote. The fix is to turn off a linter's formatting-related rules entirely and let the dedicated formatter own layout, which is exactly what a config like eslint-config-prettier does, and what combined tools like Biome and Ruff do by design, since one tool owns both jobs and cannot disagree with itself.
Order matters too: run the formatter first, then the linter, in both your editor's save action and your CI step. That way the linter is judging already-formatted code, not flagging style issues the formatter would have fixed anyway.
FAQ
Can one tool do both jobs?
Yes. Biome and Ruff are the clearest examples, formatting and linting from one binary and one config. Using separate tools such as Prettier and ESLint is still common and works fine, provided the stylistic rules are not fighting each other.
Which should run first, the formatter or the linter?
The formatter, generally. Running it first means the linter evaluates already-formatted code and will not raise style complaints the formatter was always going to fix anyway.
Does a formatter ever break code?
Essentially never, if it is working correctly. Formatters operate only on whitespace, punctuation, and layout, never on logic. If code behaves differently after formatting, that points to a formatter bug, not expected behavior.
Is it safe to auto-fix everything a linter flags?
Only for rules explicitly marked safe to auto-fix. Some linter findings require a human judgment call — an unused variable might be a leftover, or it might signal a real bug in logic that removed its only use.
Where to go next