A linter scans source for patterns that are usually mistakes — unused variables, accidental shadowing, missing await, wrong comparison operators, broken regex — and flags them as you type or in CI. It catches a class of bugs in seconds that human review would catch in hours, and ends the never-quite-resolved arguments about style by making the tool the arbiter.
← Back to Testingawait") and style enforcement ("two-space indent").--fix in pre-commit and PRs become diffs about meaning, not whitespace.| Stack | Linter(s) | Notes |
|---|---|---|
| JavaScript / TypeScript | ESLint (with typescript-eslint), Biome | ESLint is the standard. Biome is a faster all-in-one alternative (lint + format). |
| Python | Ruff, Pylint, Flake8 | Ruff has eaten most of the others — written in Rust, 10–100× faster, configurable. |
| Ruby | RuboCop | Style + correctness. Strong defaults; team-tunable. |
| Java | Checkstyle, SpotBugs, PMD, ErrorProne | Often used together; each catches different things. ErrorProne integrates with the compiler. |
| Kotlin | ktlint, detekt | ktlint for style; detekt for code-smell detection. |
| .NET (C#) | Roslyn analyzers, StyleCop | Roslyn analyzers are first-class; the IDE shows them as you type. |
| Go | golangci-lint (aggregator), staticcheck, govet | golangci-lint runs many linters in parallel. |
| Rust | clippy | Hundreds of high-quality lints; standard equipment. |
| Swift | SwiftLint | Standard for Swift codebases. |
| PHP | PHPStan, Psalm, PHP_CodeSniffer | PHPStan/Psalm are heavier static analyzers; CodeSniffer for style. |
| SQL | SQLFluff | Lints SQL across multiple dialects; auto-formats. |
| Shell | shellcheck | Catches the dozen ways shell quoting hurts you. |
| YAML / Markdown | yamllint, markdownlint | Don't ship inconsistent docs. |
Don't write your own ruleset from scratch. Start from the project's recommended config (eslint:recommended, typescript-eslint/recommended, ESLint's airbnb or standard, Ruff's defaults). Adjust as the team finds rules that don't fit; document each disable.
IDE integration shows lint errors as you type. Pre-commit hooks (Husky / lint-staged / pre-commit) catch them before push. CI runs the same check on PRs. The redundancy is the point: the earliest catch is the cheapest fix.
For style and trivial fixes, run --fix automatically. Review-time diffs become about logic, not formatting. Pair with a formatter (see formatters) to remove every "missed a space" PR comment forever.
"Warning" tier exists for migration periods. Long-term, a tolerated warning is just clutter — and gradually becomes a normal part of the build output, hiding real issues. Either fix the rule or disable it; don't leave it warning forever.
// eslint-disable-next-line no-explicit-any -- third-party API forces this. The -- reason is mandatory in many shops. A disable without a reason is a TODO that never gets done.
Some bugs are project-specific: "never call db.query directly outside the repository layer." A custom Semgrep rule, ESLint rule, or PHPStan extension catches it forever. Cheaper than writing it in the style guide and hoping people read it.
eslint-disable comments has stopped lint protection. Fix the rule fit, or accept the rule, or actually fix the code.--quiet + ratchet) to enforce on new code only.