The line between useful and annoying

A linter that your team routinely disables — or that spawns a PR full of style debates — isn't saving anyone. The goal of a lint setup isn't maximum rule coverage; it's signal-to-noise. Every rule you enable should catch something that would genuinely bite you in production or make code meaningfully harder to read. Everything else is ceremony.

ESLint's flat config format (the default since v9) makes it easier to be deliberate. Start from eslint.config.js, pull in @eslint/js, and turn on recommended. That's a tight bundle of rules — undefined variables, unreachable code, no-debugger left in — with virtually no false positives. It's a clean floor, not a ceiling.

js.js
// eslint.config.js
import js from "@eslint/js";

export default [
  js.configs.recommended,
  {
    rules: {
      "no-console": "warn",   // remind, don't block
      "eqeqeq": "error",      // == bites; === never lies
    },
  },
];

From there, add rules one at a time when a real bug surfaces, not because a blog post said to. eqeqeq is on that short list — type coercion through == produces surprises that === never would. no-unused-vars earns its keep too; dead code is noise, and noise hides bugs.

What to leave out

Style rules — indentation, quote style, semicolons — belong to a formatter like Prettier, not a linter. When ESLint handles both, you get conflicts, slower runs, and rules that produce red squiggles over genuinely irrelevant choices. Split the concerns: Prettier formats, ESLint finds bugs. The eslint-config-prettier package disables any ESLint rules that would overlap.

Plugin sprawl is the other trap. Each plugin you add comes with its own rules, its own maintenance burden, and its own opinions. eslint-plugin-react-hooks is worth it if you're using React — it catches missing dependency arrays before they cause stale closure bugs. Beyond that, be skeptical. Every plugin rule you enable is a rule someone on your team will argue about.

Making it stick

Wire ESLint into your editor (the official VS Code extension works well) so feedback is instant rather than batch. Add it to your CI pipeline as a required check — not to annoy contributors, but because a check nobody can skip is the only one that actually holds. Keep warnings few and meaningful, and treat any that linger as a prompt to either fix them or promote them to errors, so they don't quietly accumulate into a list nobody reads.

A lint config that's easy to understand is one developers will maintain rather than route around. That's the whole game. How static analysis catches bugs goes deeper if you want to add type-aware rules on top.