Introduction

Code quality tools have converged into two categories: linters (find bugs and enforce style rules) and formatters (automatically format code). The traditional approach uses separate tools for each. Newer all-in-one tools combine both in a single binary. This article compares ESLint + Prettier, Biome, and Ruff.

Linter and Formatter: ESLint, Prettier, Biome, Ruff

ESLint + Prettier

The established standard for JavaScript/TypeScript:

// eslint.config.js (flat config — v9+)

import js from "@eslint/js";

import tseslint from "typescript-eslint";

import react from "eslint-plugin-react";

export default [

js.configs.recommended,

...tseslint.configs.recommended,

{

plugins: { react },

rules: {

"react/jsx-key": "error",

"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],

"no-console": "warn",

"prefer-const": "error",

"no-var": "error",

},

ignores: ["dist/", "node_modules/ "],

},

];

// .prettierrc

{

"semi": true,

"trailingComma": "all",

"singleQuote": false,

"printWidth": 100,

"tabWidth": 2,

"arrowParens": "always",

"endOfLine": "lf"

}

Check and fix

npx eslint src/

npx eslint src/ --fix

npx prettier --check src/

npx prettier --write src/

Strengths : Largest ecosystem with 1000s of plugins, extensive customization, well-documented rules, strong community.

Weaknesses : Two separate tools require coordination. ESLint configuration can be verbose. Performance can be slow on large codebases. Running both adds build time.

Biome

A Rust-based all-in-one linter and formatter for JS/TS/JSON/CSS:

// biome.json

{

"$schema": "https://biomejs.dev/schemas/1.8.0/schema.json",

"organizeImports": {

"enabled": true

},

"linter": {

"enabled": true,

"rules": {

"recommended": true,

"complexity": {

"noBannedTypes": "error",

"noUselessConstructor": "error"

},

"correctness": {

"noUnusedVariables": "error",

"noUnusedImports": "error"

},

"style": {

"noNonNullAssertion": "warn",

"useConst": "error"

}

}

},

"formatter": {

"enabled": true,

"indentStyle": "space",

"indentWidth": 2,

"lineWidth": 100,

"formatWithErrors": false

},

"javascript": {

"formatter": {

"quoteStyle": "double",

"semicolons": "always",

"trailingComma": "all"

}

}

}

Check and format

biome check src/

biome check src/ --apply

biome format src/

biome ci src/ # CI mode (strict)

Lint only

biome lint src/

Organize imports

biome check --formatter-enabled=true --linter-enabled=true

Speed : 10-50x faster than ESLint + Prettier. A 10,000-file TypeScript project checks in ~2 seconds vs ~60 seconds for ESLint.

Strengths : Single binary (Rust), unified configuration, dramatically faster, organizes imports automatically, fewer dependencies (no separate tools).

Weaknesses : Smaller ecosystem (fewer community rules), newer (some edge cases not covered), migration effort from ESLint.

Ruff

A Rust-based linter and formatter for Python:

pyproject.toml

[tool.ruff]

target-version = "py311"

line-length = 100

[tool.ruff.lint]

select = [

"E", # pycodestyle errors

"W", # pycodestyle warnings

"F", # pyflakes

"I", # isort

"N", # pep8-naming

"UP", # pyupgrade

"B", # bugbear

"SIM", # flake8-simplify

"ARG", # flake8-unused-arguments

]

ignore = [

"E501", # Line too long (handled by formatter)

]

[tool.ruff.format]

quote-style = "double"

indent-style = "space"

skip-magic-trailing-comma = false

line-ending = "lf"

[tool.ruff.lint.per-file-ignores]

"init.py" = ["F401", "I001"]

"tests/**" = ["S101"] # Allow assert in tests

Check and fix

ruff check src/

ruff check src/ --fix

ruff format src/ # Format only

ruff check src/ --watch # Watch mode

Speed : 10-100x faster than Flake8 + Black + isort combination. Ruff replaces dozens of linting plugins in a single binary.

Strengths : Drop-in replacement for Flake8, isort, pyupgrade, and many more. Extremely fast. Single configuration file. Active development with frequent releases.

Weaknesses : Some rules not yet implemented (Pylint plugin parity). Python only.

Comparison

| Feature | ESLint + Prettier | Biome | Ruff |

|---------|------------------|-------|------|

| Language | JS/TS | JS/TS/CSS/JSON | Python |

| Language | JavaScript/TypeScript | JavaScript/TypeScript/CSS | Python |

| Architecture | Node.js (JS) | Rust | Rust |

| Speed (10K files) | ~60s | ~2s | ~1s |

| Linter + Formatter | Separate tools | Single tool | Single tool |

| Plugin ecosystem | 1000s | Growing | Comprehensive |

| Configuration | Complex | Simple | Simple |

| Migrate from | N/A | biome migrate eslint | ruff migrate |

Recommendations

  • New JS/TS projects : Start with Biome for the best developer experience and speed.

  • Existing ESLint + Prettier projects : Migration to Biome is straightforward with biome migrate eslint. Otherwise, stick with the proven combination.

  • Python projects : Ruff is the clear winner. It replaces Flake8, isort, Black, pyupgrade, and pylint in a single tool.

  • CI pipelines : Biome and Ruff reduce CI lint times from minutes to seconds.

  • Pre-commit hooks : All tools support staged-file-only checks with --staged flag.

The industry is clearly moving toward Rust-based all-in-one tools. Biome and Ruff represent the future of code quality tooling: fast, unified, and simple to configure.