Чем линтер отличается от форматтера?

JuniorFrontend инструменты #tooling #eslint #prettier

Вопрос

В чём разница между линтером (ESLint) и форматтером (Prettier), и зачем использовать их вместе?

Короткий ответ

Линтер анализирует AST кода и ищет потенциальные ошибки и нарушения стилевых правил (например, неиспользуемая переменная, == вместо ===), но не решает сам, как код должен выглядеть визуально. Форматтер не ищет ошибок вообще — он детерминированно переформатирует код (отступы, кавычки, переносы строк) по фиксированным правилам, не обсуждаемым по существу.

Подробный ответ

ESLint (линтер):

  • Парсит код в AST (абстрактное синтаксическое дерево) и прогоняет по нему набор правил (rules).
  • Правила бывают двух родов: находят реальные баги (no-undef — использование необъявленной переменной, no-unreachable — код после return, exhaustive-deps в React) и следят за стилем/качеством (no-var, eqeqeq, ограничение сложности функций).
  • Многие правила автоисправляемы (eslint --fix), но не все — часть требует решения человека (например, "эта переменная не используется — удали её или используй").
  • Конфигурируется через eslint.config.js (flat config) с наборами правил (eslint:recommended, plugin:vue/recommended и т.д.) и плагинами под конкретный стек.

Prettier (форматтер):

  • Не анализирует семантику кода и не ищет багов — он парсит и перепечатывает код в канонической форме.
  • Намеренно малонастраиваемый ("opinionated") — минимум опций (ширина строки, кавычки, точка с запятой), чтобы прекратить споры в команде о стиле форматирования в code review.
  • Идемпотентен: сколько раз ни запусти — результат один и тот же.

Зачем вместе: ESLint отвечает за "не давай писать плохой/ошибочный код", Prettier — за "весь код в репозитории выглядит одинаково, независимо от того, кто его писал". Если поручить форматирование ESLint-правилам (как раньше делали через eslint-plugin-prettier или стилевые правила вроде indent), они конфликтуют с Prettier и создают лишние ошибки в CI. Современный подход — разделение ответственности: ESLint только для качества кода, Prettier только для форматирования, стилевые правила ESLint отключаются через eslint-config-prettier.

Biome — более новая альтернатива на Rust, объединяющая линтер и форматтер в одном бинарнике без Node.js-зависимостей. Даёт кратно более высокую скорость на больших кодовых базах и не требует синхронизации версий двух отдельных инструментов, хотя экосистема плагинов пока меньше, чем у ESLint.

Пример

// eslint.config.js — линтер: ищет ошибки, не занимается форматированием
import js from '@eslint/js'

export default [
  js.configs.recommended,
  {
    rules: {
      'no-unused-vars': 'warn',
      eqeqeq: 'error',       // требовать === вместо ==
      'no-console': 'warn',
    },
  },
]
// .prettierrc — форматтер: только внешний вид кода
{
  "semi": false,
  "singleQuote": true,
  "printWidth": 100,
  "trailingComma": "es5"
}
# типичный пайплайн в CI: сначала линт (баги), потом формат-проверка (стиль)
eslint . --max-warnings=0
prettier --check .

Дополнительные вопросы

  • Почему не стоит использовать ESLint-правила для форматирования вместе с Prettier?
  • Как настроить husky + lint-staged, чтобы линт и форматирование запускались перед коммитом?
  • В чём компромисс перехода с ESLint+Prettier на Biome для большого существующего проекта?