Skip to content

What is linting?

Linting is running a linter, a tool that checks source code against rules for likely bugs and style problems without running it, usually before review.

Last updated , 8 min read

What is linting?

Linting is a static check in which a tool called a linter compares source code with a set of rules, without running the code. Each rule describes a likely mistake or a style problem, e.g. a variable that is declared and never used. The linter reports each match with its location and the name of the rule.

The name comes from lint, a Unix program that Stephen C. Johnson wrote at Bell Labs to check C source code. Wikipedia's article on lint says he borrowed the name from the small bits of fiber that clothing sheds. His program flagged code that compiled but was likely wrong or hard to port, e.g. a variable used before it was set. Later tools for other languages took the name and are called linters.

Linting is one form of static analysis. Teams usually run it before code review, so reviewers do not spend comments on problems that a tool can find.

How does a linter work?

A linter checks each file in five steps:

  1. The linter reads its configuration, which lists the rules to apply and, in many linters, a severity for each one.
  2. A parser turns the source file into a syntax tree, a structure of the file's statements and expressions.
  3. Each rule visits the nodes of the tree and checks them for the pattern it describes, e.g. a variable that no code reads.
  4. The linter prints each match as a finding that names the line and the rule.
  5. The linter sets a nonzero exit code when a finding reaches the severity that counts as a failure, e.g. error in ESLint.
How a linter checks a file Source file checkout.js Parser Syntax tree Config rules, severity Rules match patterns Findings line and rule Exit code 0 or nonzero
The rules read a syntax tree built from the source, not the running program. A hook or a pipeline acts on the exit code.

A parser reads one language, so most linters are built for one language, e.g. ShellCheck for shell scripts. Python projects often use Ruff or Pylint. ESLint, a linter for JavaScript, sorts its built-in rules into three types:

  • Possible problems. These rules flag likely logic errors, e.g. no-unreachable for code after a return statement.
  • Suggestions. These rules propose a different way to write working code, e.g. prefer-const.
  • Layout and formatting. These rules cover how code looks rather than how it runs. ESLint has deprecated most of them.

ESLint's getting started guide lists three severities that a configuration can set for each rule, off, warn, and error. A warning is printed but does not change the exit code. An error sets the exit code to 1.

What is an example of a lint rule?

Here is an illustrative example. Acme Co. sells furniture online. A developer asks a coding agent to "Let customers edit their delivery address during checkout." In Acme's code, order.setAddress rebuilds the order without its items, and order.restoreItems puts them back. The agent adds this function to src/checkout.js:

export async function changeAddress(order, address) {
  await order.setAddress(address);
  return order;
  await order.restoreItems();
}

The agent runs npx eslint src/checkout.js. The no-unreachable rule, which ESLint's recommended configuration turns on as an error, reports the line after the return statement:

/srv/acme/src/checkout.js
  4:3  error  Unreachable code  no-unreachable

The finding points to line 4, column 3, and names the rule. ESLint exits with code 1. The agent deletes line 4, runs the linter again, and gets exit code 0. A customer with 2 items in the cart then edits the address. The address saved, but the cart emptied.

The finding pointed at the bug, because the line that restores the items never ran. Moving it above the return statement would also have cleared the finding and kept the cart. The rule cannot tell which edit is right.

This example is simplified. A real project would also run a test that checks the cart after the address changes.

What changes when a coding agent writes the code?

A coding agent often runs the linter inside its loop. It reads the findings, edits the file, and runs the linter again until the exit code is 0. Lint output suits this loop, because each finding names a line and a rule.

The loop also makes exit code 0 the target. An agent can reach it with edits that remove a finding without fixing its cause:

  • Deleting the flagged code. In the example, deleting the unreachable line cleared the finding and left the cart empty.
  • Adding a disable comment. A comment on the flagged line or the line above turns the rule off there, e.g. // eslint-disable-next-line no-unreachable.
  • Editing the configuration. A change to the lint configuration turns the rule off or adds the file to an ignore list.

A team can make these edits visible. ESLint lets a disable comment carry a reason after --, which tells reviewers why the rule does not apply. In a continuous integration and delivery (CI/CD) pipeline, the --no-inline-config flag makes ESLint ignore every disable comment, including ones with a reason. Each exception then lives in the configuration file, and reviewers can treat each change to that file in an agent's diff as a finding.

Where should linting run?

Most lint rules run fast, so teams often run them at several points:

  • In the editor. An editor plugin lints the file while it changes and marks each finding in place.
  • In a Git hook. A pre-commit hook lints the staged files at each git commit. Client-side Git hooks run on each developer's machine, and git commit --no-verify skips this hook.
  • In the agent's loop. An agent hook can run the linter after each edit and send the findings back to the agent.
  • In CI. The pipeline lints the whole branch on a clean machine. The --no-verify flag does not skip this run.

In CI, the lint job is usually blocking, so an error stops the merge. A team that wants warnings to block too runs ESLint with --max-warnings 0.

Adding a rule to an old codebase can flag many files at once. ESLint can record the existing errors in an eslint-suppressions.json file and report them again only when a file's count for that rule grows. Teams then fix the old findings one rule or one folder at a time, which keeps pull request size small enough to review.

What are the limits of linting?

A linter compares code with rules that someone wrote in advance, which sets its limits:

  • It checks only what a rule describes. A bug that matches no rule passes, e.g. the empty cart once the flagged line was gone.
  • It does not run the code. A linter has no access to runtime values, e.g. what order.setAddress does to the items in the cart.
  • Some findings are wrong. A false positive flags code that is correct, and a noisy rule teaches people to ignore findings.

Lint rules can flag some leftovers of AI slop, e.g. dead code, but no rule checks a change against the request. A clean lint report means only that the code broke none of the configured rules.

How is linting different from formatting and type checking?

A formatter rewrites the layout of code, e.g. indentation, without checking for mistakes. ESLint recommends a separate formatter, e.g. Prettier. Type checking confirms that each value is used according to its declared type. Teams often run a linter with a type checker, because some likely mistakes pass a type check, e.g. an else if that repeats an earlier condition.

ESLint edits files only on request, e.g. with --fix, and only for rules with a fix. Edits that might change behavior stay as suggestions in the editor. The tools differ on these points:

PointLinterFormatterType checker
What it checksCode against a list of rulesLayout onlyValues against declared types
Changes filesOnly fixable findings, on requestYes, it rewrites the layoutNo, it only reports
OutputFindings with a line and a ruleA reformatted fileType errors with a line
Example toolESLintPrettiertsc --noEmit

FAQs

Where does the word lint come from?

The word lint comes from a Unix program named lint, written at Bell Labs to check C source code. Its author borrowed the name from the small bits of fiber that clothing sheds. Later tools for other languages took the name and are called linters.

Can a linter fix code on its own?

A linter can fix some findings on its own when the rule carries a fix. ESLint writes those fixes into the files when it runs with its fix flag. Edits that might change behavior stay as suggestions in the editor, so a person or an agent applies them.

Do linters catch bugs or only style?

Linters catch likely bugs as well as style problems. Many rules flag possible logic errors, e.g. code after a return statement that can never run. A linter does not run the code, so it misses bugs that no rule describes, e.g. a cart that empties after an address change.

How do you turn off a lint rule for one line?

A lint rule is turned off for one line with a disable comment that names the rule, on that line or the line above it. In ESLint, a short reason after the rule name must follow two hyphens, and it tells reviewers why the rule does not apply there.