guide

What Is a Linter? Linter vs Static Analysis vs Formatter (2026)

A clear explainer on what a linter is, how it differs from a formatter and full static analysis, real ESLint and Ruff examples, and where linting fits in code review.

Published:

What is a linter?

A linter is a tool that reads your source code without executing it and flags likely bugs, suspicious constructs, and style violations against a set of rules. It is the first automated line of defense in most codebases - the thing that catches an undefined variable, an unused import, or an if (x = 1) typo before the code runs or a human ever looks at it.

The name is a piece of computing history. In 1978 Stephen Johnson at Bell Labs wrote a utility called lint that scanned C programs for bugs the compiler let slide - the “lint” being the fuzz you pick off a sweater. The idea stuck, and today every major language has one or several - ESLint and Biome for JavaScript and TypeScript, Ruff and Pylint for Python, RuboCop for Ruby, golangci-lint for Go, Clippy for Rust.

A linter works by parsing your code into an abstract syntax tree - a structured representation of the code’s grammar - and then matching rules against that tree. Because it never runs the program, it is fast and safe to run on every keystroke, but it can only catch what its rules describe. The takeaway - a linter is pattern-matching over the shape of your code, which makes it fast and cheap but bounded by its rule set.

Linter vs formatter vs static analysis

These three terms get used interchangeably and they are not the same thing. Getting the distinction right tells you which tool to reach for.

Tool typeWhat it doesChanges behavior?Example
FormatterRewrites layout - indentation, line length, quotesNo, purely cosmeticPrettier, Black, gofmt
LinterFlags likely bugs and risky patterns, per fileNo, it reports (and sometimes auto-fixes)ESLint, Ruff, Pylint
Static analysis (SAST)Deep cross-file data-flow and security analysisNo, it reportsSonarQube, Semgrep

A formatter has no opinion about correctness - it makes code look consistent and ends every style argument by fiat. There is nothing to discuss because there are no options.

A linter has opinions about meaning - it will tell you a variable is never used or a promise is never awaited. Many linter rules can auto-fix, but some only warn because the right fix requires human judgment.

Full static analysis is the heavyweight cousin. Where a linter usually reasons about one file at a time with simple rules, a static analysis platform traces data across function and file boundaries to find things a linter cannot - like user input flowing into a SQL query three functions away. That cross-file taint analysis is the line between “linter” and “SAST.” The boundary is blurry, and modern tools straddle it, but the mental model holds - a linter is fast, local, and rule-based; full static analysis is slower, global, and flow-aware.

What a linter catches - real examples

Concrete beats abstract. Here is ESLint flagging a classic bug in JavaScript:

// no-cond-assign catches an assignment where a comparison was meant
if (user.role = "admin") {   // ESLint error: expected '===' not '='
  grantAccess();
}

let total = computeTotal();   // no-unused-vars: 'total' is never read
return computeTotal();

And Ruff catching problems in Python:

import os          # F401: 'os' imported but unused
import sys

def get_config(key):
    if key is "prod":        # F632: use '==' to compare literals, not 'is'
        return load_prod()
    return load_default()

Neither example is a syntax error - all of it compiles or runs. That is exactly the gap linters fill - the code the compiler accepts but a human would call a bug. Rules fall into rough buckets:

  • Probable bugs - unused variables, unreachable code, comparing with is instead of ==, missing await.
  • Risky patterns - shadowed variables, implicit type coercions, mutable default arguments.
  • Style and consistency - naming conventions, import ordering, max line length (though much of this is better left to a formatter).
  • Light security - some linters flag obvious issues like eval on user input, though deep security is SAST territory.

Where linting fits in the workflow

A linter delivers value in proportion to how early it runs. The ideal is a layered setup:

  1. In the editor. The linter runs as you type and underlines problems instantly. This is where developer experience lives - you fix the issue before it is even committed.
  2. As a pre-commit hook. A hook runs the linter (often only on changed files) before a commit lands, so obvious issues never enter history.
  3. In CI as a required check. The enforcement gate. Even if someone skips the local hook, CI runs the linter and blocks the merge on failure. This is what makes the standard non-optional.

Running it only in CI is a common mistake - developers discover lint failures after pushing, which is slow and annoying. Running it only locally is the opposite mistake - anyone can skip the hook. You want both. The takeaway - lint locally for speed and in CI for enforcement; skipping either layer costs you either developer experience or reliability.

Linters and code review

Here is why linting matters for reviewers - every issue a linter catches is an issue a human does not have to comment on. When formatting and lint rules are automated, review comments stop being about spacing and start being about design and correctness. A reviewer arguing about import order is a wasted reviewer. Automate the mechanical layer and you free human attention for the judgment calls machines cannot make - the same principle behind a good code review checklist.

Beyond the linter - tools that go further

When you outgrow a single-file linter, several platforms fold linting into deeper analysis and pull the results into code review.

SonarQube static analysis tool homepage screenshot
SonarQube homepage

SonarQube is the industry-standard example - it runs 6,000-plus rules across 35-plus languages and pairs linting with deeper bug and security detection, then gates merges through quality gates (source - SonarQube tool page). Semgrep sits between a linter and a SAST engine - it lets you write custom pattern-matching rules with a lightweight, grep-like syntax across 30-plus languages, so you can encode your own team conventions as rules (source - Semgrep tool page).

DeepSource code quality platform homepage screenshot
DeepSource homepage

DeepSource runs 5,000-plus analyzers with a sub-5 percent false-positive rate and an Autofix capability that remediates many findings automatically (source - DeepSource tool page). And Sourcery leans into refactoring - it flags smelly patterns and suggests concrete rewrites in pull requests, with especially strong Python support and free access for open-source projects (source - Sourcery tool page). For language-specific picks, see our roundups for JavaScript and Python.

Common mistakes with linters

  • Treating warnings as noise. A linter with 4,000 ignored warnings is worse than no linter - the real signal is buried. Fix or explicitly suppress; do not let warnings pile up.
  • Confusing lint with formatting. Let a formatter own layout and a linter own correctness, so the two do not fight over the same lines.
  • No shared config. If each developer has different rules, review devolves into style debates. Commit one config to the repo.
  • Expecting a linter to find deep bugs. A per-file linter will not catch a cross-service data-flow vulnerability. That is what SAST and full static analysis are for.

Conclusion

A linter is the fast, rule-based first pass that reads your code without running it and flags likely bugs and risky patterns - distinct from a formatter, which only fixes layout, and from full static analysis, which traces data across files to find deeper issues. Run it in the editor, on pre-commit, and in CI so it catches problems early and enforces the standard automatically. Do that well and your linter quietly removes an entire category of review comments, freeing human reviewers to focus on the design and correctness questions that actually need a person. When you need more depth, platforms like SonarQube and Semgrep pick up where the linter stops.

Further reading

Sponsored Why?
Gitar logoGitar

Comments are not enough

Gitar applies the fix, validates it in CI, and clears the queue.

See it on your repo Read our independent Gitar review

Frequently Asked Questions

What is a linter in programming?

A linter is a tool that analyzes source code without running it and flags likely errors, suspicious patterns, and style violations. The name comes from a 1978 Unix utility called lint that scanned C code for bugs the compiler ignored. Modern linters like ESLint, Ruff, and Pylint check for undefined variables, unused imports, unreachable code, and hundreds of rule-based patterns before the code ever runs.

What is the difference between a linter and a formatter?

A formatter only rewrites how code looks - indentation, line length, quote style - without changing what it does, and it never asks for your opinion. A linter analyzes what the code means and flags likely bugs and risky patterns, some of which it cannot fix automatically. Prettier and Black are formatters; ESLint and Pylint are linters. Most teams run both - the formatter for layout, the linter for correctness.

Is a linter the same as static analysis?

A linter is one kind of static analysis - the fast, rule-based, single-file kind. Full static analysis platforms go further with cross-file data-flow and taint analysis to find deep bugs and security vulnerabilities that a per-file linter cannot see. Think of a linter as lightweight static analysis you run on every save, and a SAST platform as heavyweight analysis you run in CI.

Should linting run locally or in CI?

Both. Run the linter in the editor and as a pre-commit hook so developers get instant feedback and fix issues before pushing, and run it again in CI as a required check so nothing slips through if a local hook is skipped. The local run is for speed and developer experience; the CI run is the enforcement gate that actually blocks a merge.

Explore More

Free Newsletter

Stay ahead with AI dev tools

Weekly insights on AI code review, static analysis, and developer productivity. No spam, unsubscribe anytime.

By subscribing you agree to receive the weekly newsletter. Unsubscribe in one click, any time. See our privacy policy.

Join developers getting weekly AI tool insights.

Related Articles