Skip to Content
JavaScript Advanced: Code That Holds Up in Production
Advanced
0 likes
Courses
JavaScript Advanced: Code That Holds Up in Production

ESLint in a Real Project

Set up ESLint with the flat config, pick rules that catch bugs instead of arguing about style, and fix or silence findings properly. After this, the linter finds the bug in review instead of production.

Course content

Sign in to save your progress.

Course Home

Skip ahead: If ESLint already runs in your project and you want to know how to adopt it on old code, skip to "An old project with hundreds of errors".

A linter reads your code without running it and points out patterns that are almost always bugs: a variable that's never defined, an = where you meant ===, an await that does nothing. ESLint is the standard one for JavaScript. Set up well, it catches mistakes in seconds. Set up badly, it argues about semicolons and the team turns it off.

Setup

ESLint 9 and later uses one eslint.config.js file (the "flat config"). ESLint 10 no longer reads old .eslintrc files at all.

npm install --save-dev eslint @eslint/js globals
import js from "@eslint/js";
import globals from "globals";

export default [
  js.configs.recommended,
  {
    files: ["**/*.js"],
    languageOptions: {
      globals: { ...globals.node },
    },
  },
];

js.configs.recommended is the set of rules that find real problems. globals.node tells ESLint that process and console exist. For browser code, use globals.browser.

What it finds

Here's a function with two bugs that look fine at a glance:

import { readFile } from "node:fs/promises";

export async function loadReport(path) {
  const text = await readFile(path, "utf8");
  const rows = text.split("\n");
  let total = 0;
  for (const row of rows) {
    const [status, amount] = row.split(",");
    if (status = "paid") total += Number(amount);
  }
  return totl;
}

npx eslint . (ESLint 10.12; the first line of its output, the file's full path, is left out here):

   9:9   error  'status' is constant                                             no-const-assign
   9:9   error  Expected a conditional expression and instead saw an assignment  no-cond-assign
   9:9   error  Unexpected constant condition                                    no-constant-condition
   9:9   error  'status' is assigned a value but never used                      no-unused-vars
   9:26  error  'total' is assigned a value but never used                       no-unused-vars
  11:10  error  'totl' is not defined                                            no-undef

✖ 6 problems (6 errors, 0 warnings)

Six messages, two bugs: = instead of === on line 9, and a typo on line 11. The other messages follow from those. Line 9 would crash at runtime because status is a const. Line 11 would crash too. Neither crash happens until that code runs, which in a rarely used report could be weeks.

Rules: bugs first, style later

recommended is mostly bugs. Add a few more that pay for themselves:

rules: {
  eqeqeq: "error",
  "no-var": "error",
  "prefer-const": "error",
}

Leave formatting (quotes, semicolons, indentation) to Prettier or your editor's formatter. Linters that fight about style get ignored, and then they stop catching bugs too.

Some rules can fix their own findings. npx eslint --fix . changes var to let and let to const where it's safe. Run it, then read the diff before committing.

An old project with hundreds of errors

Turning on ESLint for the first time in a 3-year-old project can give you 500 errors. Nobody fixes 500 errors in one go, and a CI check that fails on day one gets disabled on day two. Do it in this order:

  1. Run npx eslint --fix . and commit that alone. It's mechanical and easy to review.
  2. Look at what's left by rule. A handful of rules usually make up most of the count.
  3. Record the existing problems as a baseline: npx eslint --suppress-all . writes eslint-suppressions.json. From then on, npx eslint . passes with the old problems and fails on new ones.
  4. Fix the suppressed problems a file at a time, when you're working in that file anyway. npx eslint --prune-suppressions . removes entries that are already fixed.
$ npx eslint --suppress-all .
$ npx eslint .
$ echo "export const z = (a) => b;" > src/new.js
$ npx eslint .
  1:19  error  'a' is defined but never used  no-unused-vars
  1:25  error  'b' is not defined             no-undef

✖ 2 problems (2 errors, 0 warnings)

The old file with six problems passes. The new file fails. That's the behaviour you want: the codebase can only get better.

💡 Tip: Commit eslint-suppressions.json. It's the team's list of known problems, and it shrinks as people fix them.

Silencing one line

When a rule is wrong for one line, disable it on that line, with the reason after --:

// eslint-disable-next-line no-await-in-loop -- pages must be fetched in order
const page = await fetchPage(next);

Never put /* eslint-disable */ at the top of a file to make the errors go away. It also hides the next real bug in that file.

Run it everywhere

  • In the editor, so findings show while you type (the ESLint extension in VS Code).
  • In package.json: "lint": "eslint .".
  • In CI, before tests. A pull request with lint errors doesn't merge.

⚠ Warning: ESLint isn't a type checker. It doesn't know that cartTotal("10%") passes the wrong type. Run it together with tsc --noEmit from the previous lesson. They catch different things.

Common mistakes

  • Starting with a strict style guide and getting 2,000 messages about spacing.
  • Disabling whole files or whole rules to get CI green.
  • Running it only in CI, so people find out after they push.
  • Mixing --fix changes with real changes in one commit.

What happens if you skip this

= instead of === and a misspelled variable go through review, because humans read what they expect to see. The linter doesn't.

Which of the six messages in the report example would you have noticed in a code review without the linter?

About

Set up ESLint with the flat config, pick rules that catch bugs instead of arguing about style, and fix or silence findings properly. After this, the linter finds the bug in review instead of production.

Duration
5 min
Last update
08/17/2026
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.