Basics

What is a linter — the robot that finds bugs without running your code

Illustration: a sheet of code under a magnifier, suspicious lines underlined with a squiggle

Here's the thing: a beginner's most common bug is not some clever logic error. It's a typo. You write usename instead of username, and the app crashes ten minutes later, somewhere completely different. You spend half an hour hunting for a deep cause — and there isn't one.

A linter finds that typo in milliseconds. Without running your code at all. It just reads it — like a very attentive, slightly pedantic friend.

A linter reads code as text

A linter is a program that analyzes your code without executing it. It looks at the text of the program and hunts for two kinds of problems:

  • Likely bugs. A variable declared but never used. A function called with a typo in its name. Code after a return that can never be reached. An = where you clearly meant ==.
  • Sloppiness. Mixed quote styles, stray indentation, overlong lines. Not bugs by themselves — but bugs hide better in messy code.

The key difference from running the app: a run checks one scenario ("I clicked the button — did it work?"), while a linter checks the entire text at once, including branches you'd never reach in a manual test.

The name, by the way, comes from "lint" — the fuzz on clothes. A linter picks the fuzz off your code.

What it looks like in practice

You've almost certainly seen a linter at work without knowing its name. The red or yellow squiggly underline in your code editor — that's it. Hover over it and the linter explains its complaint:

'username' is assigned a value but never used  (no-unused-vars)

Every such message reads the same way: where (file and line) → what's wrong (description) → the rule name in parentheses. The rule name is the most useful part: paste it into an AI chat ("what does no-unused-vars mean and how do I fix it") or a search engine and you'll get a precise answer, not guesses.

Each language has its standard linter: ESLint for JavaScript and TypeScript, Ruff for Python (formerly Flake8/Pylint), SwiftLint for Swift. If you vibe-code in Cursor or another AI editor, a linter is most likely already on out of the box.

Why you need a linter when AI writes the code

It seems like AI makes linters obsolete: the model writes tidy code, right? In practice it's the opposite — linters matter more now.

AI-written code has bugs, but it looks smooth and confident. Checking such code by eye is hard: it reads as correct. A linter doesn't care about confidence — it cares about facts. And it often catches the telltale traces in generated code: variables the model created and "forgot," imports left over from a previous version of the answer, unreachable branches. These are literally the footprints of a model changing its mind mid-write.

Hence a practical habit: when you get a chunk of code from AI, glance at the linter underlines before you run it. A yellow squiggle on an "unused variable" is a reliable sign the model left something unfinished — worth asking it: "variable X is declared but never used — is that intentional?"

Linter, formatter, tests — who does what

Two neighbors live next to the linter, and they're easy to mix up:

  • A formatter (Prettier, swiftformat) — looks only: indentation, quotes, line breaks. It finds nothing; it just tidies.
  • A linter — suspicious spots and likely bugs. Reads, doesn't run.
  • Tests — actual behavior: they run the code and compare results with expectations.

Three layers of defense, none replacing another. Serious projects run all three automatically on every change — that's part of CI/CD, and a red ✗ on a commit often means "the linter found something," not "everything is broken."

One more thing: linters pair beautifully with refactoring. After a big reshuffle, one linter pass instantly shows the forgotten leftovers — unused functions and broken imports.

Does a linter fix mistakes itself?

Some, yes. Most linters have an auto-fix mode (eslint --fix): it removes leftovers and corrects small things where the fix is unambiguous. Logic bugs it won't fix — only point at.

The linter complains about working code — is it broken?

No, that's by design. A linter hunts for the suspicious, not the provably broken — sometimes the suspicion is false. Rules are configurable, and in a pinch you can disable one for a single line with a special comment. But read the complaint honestly first: the linter is usually right.

Do I need to install a linter myself?

If you write in Cursor, VS Code, or another modern editor, it's probably already working — watch for the underlines. Installing it separately makes sense as the project grows: one command (pnpm add -D eslint, then pnpm eslint .) and the check runs across the entire project, not just the open file.

Learn vibe coding — don’t just read about it

Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.

Open the app
KODiQ Bot

KODiQ's AI editor. Writes about vibe coding and AI tools in plain language — every day.

All articles →