Skip to content

Split View: 2026년의 포매터·린터 — Biome, dprint, Ruff, oxc, Prettier 4, ESLint v9의 진짜 위치

|

2026년의 포매터·린터 — Biome, dprint, Ruff, oxc, Prettier 4, ESLint v9의 진짜 위치

프롤로그 — 왜 또 포매터·린터 이야기인가

prettier --write . 한 줄로 끝나던 시대가 있었다. 2020년쯤이다. 그 후 5년이 지나는 동안 같은 명령이 모노레포에서 30초, CI에서 2분으로 부풀었다. eslint --max-warnings 0 은 그것보다 더 느렸고, 어느 순간 우리는 "사전 커밋 훅에서 lint를 빼야 하나"를 진지하게 토론하고 있었다.

같은 시기 Python 진영에서도 비슷한 통증이 있었다. black + flake8 + isort + pylint 4단 콤보는 안정적이지만 느렸다. 큰 저장소에서 한 번 돌리는 데 분 단위가 걸렸고, 4개 도구의 설정 파일이 따로 놀았다.

여기에 Rust 진영의 도구 작성자들이 답을 들고 왔다. 속도가 진짜 100배 빠르면 다른 모든 것이 달라진다는 답이었다. Ruff(2022)가 Python에서 그걸 증명했고, Biome(이전의 Rome)이 JS/TS에서 같은 길을 갔다. dprint, oxc, ty(타입 체커), uv(패키지 매니저)까지 — Astral과 Biome 팀, 그리고 Boshen(oxc) 같은 작은 팀들이 이끄는 Rust 재작성의 물결이다.

2026년 현재, Prettier 4가 Rust 플러그인 길을 공식적으로 받아들였고, ESLint v9의 flat config가 안정화됐으며, typescript-eslint v8는 단일 패키지로 다시 묶였다. Biome 2는 multi-file 분석과 import sorting을 정식 기능으로 갖췄고, Ruff는 사실상 Python 린팅의 기본값이 됐다.

이 글은 그 지형을 정리한다. 어느 도구가 어느 자리에서 무엇을 잘하는가, "Rust로 다시 짜면 다 좋아진다"는 명제가 어디까지 진실인가, 그리고 우리 팀에 무엇이 맞는가. 마케팅이 아니라 측정 가능한 트레이드오프로.

핵심 한 줄: 도구는 빨라졌고 통합됐다. 하지만 "Biome 한 줄로 다 해결" 같은 단순한 그림은 아직 아니다. 어떤 트레이드오프를 받아들이느냐가 선택을 결정한다.


1장 · 지형도 — 2026년의 포매터·린터 전체 그림

먼저 큰 그림을 그린다. 언어별로 어떤 선택지가 있고, 누가 어디서 잘 도느냐.

언어 / 영역전통적 도구2026년 주력차세대 후보
JS / TSPrettier + ESLintPrettier + ESLint v9, Biome 2oxc, dprint
Pythonblack + flake8 + isort + pylintRuff (format + lint)ty (타입 체커)
Rustrustfmt + clippyrustfmt + clippy— (이미 Rust)
Gogofmt + golangci-lint동일
CSS / SCSSPrettier + stylelintPrettier + stylelint, Biome 2dprint, oxc
MarkdownPrettier, remark-lintPrettier, dprint
TOML / YAML / JSONPrettierdprint, taplo, yamlfmt
다중 언어 모노레포Prettier + ESLintdprint(통합 포매터), Biome

카테고리별 한 줄 정리

  • 포매터: 코드의 모양(공백·줄바꿈·따옴표·세미콜론)을 결정한다. 의견을 갖고 강제한다. Prettier, Biome 포맷, dprint, rustfmt, gofmt, Ruff format, black이 여기에 속한다
  • 린터: 의미적 문제(미사용 변수, 잠재적 버그, 안티패턴)를 잡는다. ESLint, Biome 린트, Ruff lint, clippy, golangci-lint, stylelint, pylint
  • 타입 체커: 타입 오류를 잡는다. tsc, mypy, pyright, ty(Astral), Flow
  • 포매터·린터 통합 도구: 한 바이너리에서 둘 다 하는 흐름의 신세대. Biome, Ruff가 대표

이 글의 주인공은 세 번째 흐름 — Rust로 짜인 차세대 통합 도구들. 그 도구들이 기존 도구를 대체하는 곳, 보완하는 곳, 아직 못 가는 곳을 본다.

한 줄 요약: 언어마다 사정이 다르다. Python은 Ruff가 사실상 정답이 됐고, JS/TS는 아직 Biome vs Prettier+ESLint의 결정이 남아 있고, 모노레포는 dprint가 새로운 선택지로 들어왔다.


2장 · Rust 재작성 패턴 — 왜 10~100배 빠른가

먼저 "왜 빠른가"를 짚는다. 마케팅이 아니라 구조의 차이다.

측정 가능한 속도 차

Biome 공식 문서가 보여주는 수치 — 단일 파일 포맷에서 Biome은 Prettier보다 25배35배 빠르고, 큰 저장소에서 더 큰 격차가 난다. Ruff는 같은 규칙으로 flake8 대비 약 100배 빠른 것이 자주 보고된다(중간큰 Python 저장소에서 분 단위가 초 단위로).

실제 사용 보고에서 자주 보이는 수치:

  • Prettier 모노레포 포맷 30초 → Biome 1~2초
  • flake8 + black 모노레포 점검 90초 → Ruff 2~3초
  • ESLint flat config 모노레포 lint 45초 → Biome 3~5초 (규칙 커버리지는 다름, 후술)
  • Prettier 단일 파일 포맷 300ms → dprint/Biome 5~15ms

왜 이 정도 차이가 나나

  1. 언어 자체의 차 — JS 도구는 V8 JIT 위에서 도는 인터프리터 코드, Rust 도구는 네이티브 바이너리. 이것만으로도 3~10배
  2. 단일 패스 파싱 — 전통 도구는 lex→parse→AST 변환을 여러 번 한다. Biome/oxc/Ruff는 한 번의 패스로 lossless concrete syntax tree(CST)를 만들고 모든 분석에 재사용
  3. 병렬화 — Rust의 데이터 동시성을 살려 파일을 진짜 병렬로 처리. JS 도구는 단일 스레드, Worker 풀을 쓰더라도 직렬화 비용
  4. 메모리 레이아웃 — CST 노드를 cache-friendly한 배열에 저장(rowan/oxc-resolver 같은 라이브러리). Allocation이 적음
  5. 재시작 비용 없음 — Rust 바이너리는 시작이 15ms, Node는 50200ms. CLI 반복 호출에서 큰 차이

빠름의 2차 효과

  • 사전 커밋 훅에서 다시 돌릴 수 있게 됨 — 30초였던 게 1초가 되면 훅에 다시 넣어도 됨
  • 에디터 통합이 라이브가 됨 — 타이핑할 때마다 린트가 가능
  • CI에서 따로 캐싱할 필요가 줄어듦 — 어차피 빠르니까
  • monorepo 전체 lint가 일상이 됨 — 분 단위면 CI에서만, 초 단위면 로컬에서도

한 줄 요약: 빠름 자체보다 빠름이 가능하게 하는 워크플로 변화가 본질이다. "어차피 느려서 못 함"이 풀린다.


3장 · Biome 2 — 포매터·린터 통합의 선두

Biome은 가장 야심적인 통합 도구다. 원래 이름은 Rome(2020, Sebastian McKenzie — Babel 만든 사람), 자금 부족으로 정지, 2023년 커뮤니티 포크로 Biome 출범, 2024년 Biome 1.0, 2025년 Biome 2.0.

Biome 2의 핵심 약속

  • JS / TS / JSX / TSX / JSON / CSS / GraphQL 포맷 + 린트 한 바이너리
  • 단일 설정 파일 biome.json — Prettier+ESLint 두 개를 통합
  • 타입 인식 린팅 — typescript-eslint와 비슷한 type-aware 규칙 일부 (V2에서 multi-file 분석 추가)
  • 임포트 정렬 — eslint-plugin-import 일부를 대체
  • diff format — 변경된 부분만 포맷 가능 (CI에서 큰 PR에 유용)
  • 마이그레이션 도구 biome migrate prettier, biome migrate eslint

설정 예시

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
  "files": { "ignoreUnknown": true },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "javascript": {
    "formatter": { "quoteStyle": "single", "semicolons": "asNeeded" }
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": { "noUnusedVariables": "error" },
      "style": { "useImportType": "warn" },
      "suspicious": { "noExplicitAny": "warn" }
    }
  },
  "assist": {
    "enabled": true,
    "actions": { "source": { "organizeImports": "on" } }
  }
}

Biome가 잘하는 곳

  • JS/TS 위주, 단순한 규칙 세트 — 90% 규칙 커버리지면 충분한 팀
  • 단일 설정의 가치를 알아보는 팀 — Prettier+ESLint 설정의 복잡성에 지친 팀
  • CI 속도가 진짜 문제인 팀 — 모노레포 lint가 분 단위 걸리는 경우
  • 로컬 IDE 통합 속도 — VS Code 익스텐션이 즉시 반응

Biome가 못 가는 곳 / 아직 부족한 곳

  • ESLint 플러그인 생태계 부재 — eslint-plugin-react-hooks, eslint-plugin-jest, eslint-plugin-tailwindcss, eslint-plugin-import 같은 외부 규칙을 직접 못 씀. Biome가 일부를 내장으로 흡수 중이지만 ESLint 플러그인 1:1 대체는 어려움
  • Vue, Svelte 등 비표준 파일 포맷 미지원.vue, .svelte는 아직 (커뮤니티 플러그인 일부 있으나 공식 미지원)
  • Markdown / YAML / TOML 포맷 미지원 — Prettier가 커버하던 영역을 못 함
  • 타입 인식 린팅의 한계 — typescript-eslint의 전체 규칙 세트를 따라잡지 못함
  • 마이그레이션 비용 — 큰 저장소에서 ESLint 규칙 차이를 손으로 메우는 데 며칠

결정 기준

팀 상황Biome 적합도
새 JS/TS 프로젝트, 단순 규칙매우 높음
모노레포, CI 속도 중요, Vue/Svelte 없음높음
기존 Prettier+ESLint, 만족 중옮길 동기 약함
eslint-plugin-* 깊이 활용아직 어려움
Vue / Svelte / Astro / MDX 비중 큼추천 안 함

한 줄 요약: Biome는 "JS/TS만 보면" 가장 매력적인 통합 도구다. 단, 플러그인 생태계와 비표준 파일 지원에서 Prettier+ESLint를 완전히 대체하기에는 아직 빈자리가 있다.


4장 · dprint — 플러그인 기반 다중 언어 포매터

dprint는 다른 접근이다. 자체적으로는 작은 코어, 언어별 플러그인으로 확장한다. 단일 도구로 JS/TS, JSON, Markdown, TOML, Dockerfile, SQL을 한 번에 포맷한다.

dprint의 차별점

  • WASM 플러그인 시스템 — 언어 지원이 플러그인. 코어는 빠르고 작음
  • 다중 언어 모노레포 최적 — Prettier 안 되는 TOML, Dockerfile까지 한 도구로
  • Rust 작성 + Rust/WASM 플러그인 SDK
  • 순수 포매터 — 린팅은 안 함. 한 가지만 잘함

설정 예시

{
  "lineWidth": 100,
  "indentWidth": 2,
  "useTabs": false,
  "typescript": {
    "quoteStyle": "preferSingle",
    "semiColons": "asi"
  },
  "json": {
    "lineWidth": 200
  },
  "markdown": {
    "textWrap": "always"
  },
  "toml": {},
  "dockerfile": {},
  "plugins": [
    "https://plugins.dprint.dev/typescript-0.93.0.wasm",
    "https://plugins.dprint.dev/json-0.20.0.wasm",
    "https://plugins.dprint.dev/markdown-0.18.0.wasm",
    "https://plugins.dprint.dev/toml-0.7.1.wasm",
    "https://plugins.dprint.dev/dockerfile-0.3.4.wasm"
  ],
  "includes": ["**/*.{ts,tsx,js,jsx,json,md,toml,Dockerfile}"],
  "excludes": ["**/node_modules", "**/dist"]
}

dprint가 잘하는 곳

  • 다중 언어 모노레포 — TS + JSON + Markdown + TOML + Dockerfile 등을 한 도구로
  • 순수 포매팅 가치 — 린팅까지 안 통합해도 됨, 그냥 빠른 Prettier 대체
  • 플러그인 격리 — 각 언어가 WASM 모듈로 격리되어 의존성 충돌 없음
  • 버전 고정의 편의dprint config update로 플러그인 버전 한 번에 갱신

dprint가 못 가는 곳

  • 린팅 없음 — 린터는 별도 (ESLint, Biome 린트, Ruff 등)
  • JS/TS 규칙 커버리지 — Prettier보다는 살짝 적음. 옵션이 더 의견 있음
  • 에디터 통합 성숙도 — VS Code 확장은 있으나 Prettier만큼 부드럽지는 않음
  • CSS / GraphQL 플러그인 상태 — 일부 플러그인은 베타 또는 커뮤니티 유지

누가 쓰나

  • Deno 표준 코드베이스(dprint 자체가 Deno 친화적)
  • 인프라 저장소(IaC, Docker, K8s, Terraform 옆에 두기 좋음)
  • 다국어 모노레포(TS + Python + 인프라 코드 함께 사는 곳)

한 줄 요약: dprint는 "한 도구로 모든 텍스트 포맷"을 노린다. 린팅 없이 포맷만, 다중 언어가 진짜로 섞인 모노레포에서 빛난다.


5장 · Ruff — Python을 평정한 Rust 도구

Ruff는 가장 압도적인 성공 사례다. Charlie Marsh(Astral)가 2022년 첫 릴리스, 2026년 현재 PyPI Python 도구 다운로드 통계에서 사실상 1위. Django, Pandas, FastAPI, Pydantic, Apache Airflow, scikit-learn 같은 메이저 프로젝트가 Ruff를 채택했다.

Ruff의 약속

  • flake8 + isort + pydocstyle + pyupgrade + bandit + autoflake + black 대체 — 7~10개 도구를 1개로
  • 800개 이상의 린트 규칙 — flake8 + 주요 플러그인 규칙 대부분 이식
  • 포매터 내장ruff format이 black 호환 (black의 고집스러운 의견을 거의 그대로 따름)
  • import 정렬 — isort 대체
  • 자동 수정ruff check --fix로 안전한 수정 자동화
  • 단일 설정pyproject.toml[tool.ruff] 섹션 하나에

설정 예시

[tool.ruff]
line-length = 100
target-version = "py312"
extend-exclude = ["migrations", ".venv", "build"]

[tool.ruff.lint]
select = [
  "E", "W",      # pycodestyle
  "F",           # pyflakes
  "I",           # isort
  "N",           # pep8-naming
  "UP",          # pyupgrade
  "B",           # flake8-bugbear
  "C4",          # flake8-comprehensions
  "DTZ",         # flake8-datetimez
  "S",           # flake8-bandit (security)
  "RUF",         # ruff-specific
]
ignore = ["E501"]  # line-too-long handled by formatter
fixable = ["ALL"]
unfixable = []

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"]  # allow assert in tests
"__init__.py" = ["F401"]  # allow unused imports

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true

Ruff가 잘하는 곳 — 거의 모든 곳

  • 속도 — flake8+pylint 90초 → Ruff 2초
  • 단일 도구 — pre-commit 설정이 7줄에서 1줄로
  • 자동 수정의 범위 — flake8은 보고만 하던 것을 Ruff는 고침
  • CI 캐싱 불필요 — 어차피 빠름
  • flake8 규칙 셀렉터 호환 — 마이그레이션 비용이 낮음

Ruff의 한계 / 차이

  • mypy 대체는 아님 — 타입 체킹은 별도. Astral이 ty(타입 체커, Rust)를 개발 중이지만 2026년 현재 알파/베타 단계
  • black과 100% 동일하지는 않음 — 95%는 같지만 엣지 케이스(긴 문자열, 복잡한 표현식) 일부 차이. 한 번 ruff format을 돌리면 diff가 발생할 수 있음 — 대규모 PR이 한 번 필요
  • pylint의 깊은 분석 일부 미흡too-many-locals, cyclomatic-complexity 같은 규칙은 제한적
  • 플러그인 시스템 없음 — 모든 규칙이 코어에 내장. 커스텀 규칙 추가가 어려움

Astral의 큰 그림 — Rust 재작성 전체 스택

Charlie Marsh의 야심은 Ruff에 그치지 않는다. Astral은 Python 도구 체인 전체를 Rust로 다시 쓰고 있다.

도구대체 대상상태(2026)
Ruffflake8, isort, black, pydocstyle, ...안정, 광범위 채택
uvpip, virtualenv, pip-tools, pipx, poetry(일부)안정, 빠르게 확산
tymypy, pyright(부분)알파/베타
ryepoetry, hatch(개념적)uv에 흡수되는 중

전체 패턴: "도구 하나가 빠르면 그 도구 위주로 모든 워크플로가 재편된다." 같은 패턴이 Biome(JS), oxc(JS)에서 반복된다.

한 줄 요약: Ruff는 Python 도구 체인에서 사실상 디폴트가 됐다. 이유는 단순하다 — 빠르고, 통합돼 있고, 마이그레이션 비용이 낮다.


6장 · oxc — Rust JavaScript 도구 체인 가족

oxc는 다른 야심이다. Boshen이 이끄는 프로젝트로, JavaScript 도구 체인 전체를 Rust로 만든다. 파서, 리졸버, 트랜스포머, 린터, 포매터, 미니파이어. Biome는 "단일 사용자 도구"라면, oxc는 "다른 도구가 쓰는 컴포넌트 라이브러리"에 가깝다.

oxc의 구성 요소

컴포넌트무엇비교 대상
oxc-parserJS/TS 파서acorn, esprima, swc 파서
oxc-resolver모듈 해석enhanced-resolve
oxc-transformerJS/TS 변환Babel, swc
oxlint린터ESLint
oxc-prettier포매터(Prettier Rust 포트)Prettier
oxc-minifier미니파이어terser, swc, esbuild

oxlint — ESLint를 노린 Rust 린터

oxlint는 ESLint 코어 규칙과 플러그인 일부를 Rust로 다시 쓴다. 2026년 현재 oxlint는 ESLint v9 호환 모드를 일부 지원하며, 빠른 사전 점검(pre-flight check) 용도로 권장된다.

# 빠른 lint (oxlint), 깊은 lint (ESLint)
oxlint --deny-warnings src/
# ESLint 호환 보고
oxlint --rules-include=react-hooks src/

oxc가 잘하는 곳

  • 빌드 도구 작성자 / 프레임워크 작성자 — Rspack, Rolldown 같은 도구가 oxc를 내부적으로 채용. 빠른 컴포넌트 라이브러리
  • 사전 점검 린트 — Lefthook/Husky 사전 커밋 훅에서 ESLint 전체 대신 oxlint로 70% 규칙을 0.5초에 점검
  • CI 첫 단계 — 빠른 실패. oxlint 통과 후 ESLint 깊은 점검
  • Prettier 호환 포매터 — Prettier 4의 Rust 플러그인 경로의 한 축

oxc의 한계

  • 플러그인 시스템 미성숙 — ESLint 플러그인을 직접 못 씀
  • 린트 규칙 커버리지 부분적 — ESLint의 전체 규칙 1:1 매핑 아직
  • 단독 도구로 쓰기 어려움 — Biome처럼 "이것만 쓰면 끝"이 아니라 보완재
  • 포매터는 Prettier 4 통합 경로에 더 가까움 — 독립 사용 사례 적음

Biome vs oxc — 무엇이 다른가

비교Biomeoxc
목표통합 사용자 도구컴포넌트 라이브러리 + 도구 가족
단일 설정그렇다 (biome.json)도구마다 다름
사용자 경험"Prettier+ESLint 대체""ESLint 빠른 보완"
플러그인내장 위주(계획) WASM 플러그인
누가 채용애플리케이션 팀빌드 도구 작성자, 프레임워크

한 줄 요약: Biome가 사용자 도구, oxc는 컴포넌트 가족. 사용자 입장에서는 Biome가 직관적, 도구 작성자 입장에서는 oxc가 유용. 둘은 경쟁이 아니라 다른 층위에서 일한다.


7장 · Prettier 4 — 황제는 어떻게 응답했는가

Prettier는 2017년 이후 사실상 JS/TS 포매터의 디폴트였다. 의견을 가지고 강제하고, 설정이 거의 없는 것이 핵심 철학. 그 결과 거의 모든 프로젝트가 채택했다.

하지만 속도 문제가 누적됐다. Prettier 3까지는 순수 JS, Node 위에서 도는 도구. 빠른 도구들과의 격차가 커지자, Prettier 팀은 2025년부터 Rust 플러그인 경로를 공식화했다.

Prettier 4의 핵심 변화

  • Rust 기반 파서·포매터 경로 — JS/TS 코어는 oxc 또는 swc 기반 Rust 백엔드를 채용할 수 있는 어댑터 추가
  • 호환성 우선 — Prettier 3의 출력과 비트 단위 일치가 목표 (95%+ 일치). 100% 가는 길은 점진적
  • 플러그인 API 안정화 — Markdown, YAML, GraphQL, Tailwind 같은 플러그인은 그대로
  • 속도 개선 — Rust 백엔드 활성 시 5~15배 가속 (Biome만큼은 아니지만 의미 있음)
  • 단일 설치 모델 유지npm install prettier 한 줄로 끝나는 사용자 경험은 유지

설정 예시 (Prettier 4)

{
  "experimentalRustBackend": true,
  "printWidth": 100,
  "singleQuote": true,
  "semi": false,
  "trailingComma": "all",
  "plugins": [
    "prettier-plugin-tailwindcss",
    "prettier-plugin-organize-imports"
  ]
}

Prettier가 살아남는 이유

  • 생태계의 관성 — Tailwind 정렬, import 정렬, Astro/Svelte/Vue 플러그인 등 수백 개의 플러그인
  • 에디터 통합의 완성도 — VS Code "format on save"가 가장 안정적
  • 의견의 의견 — Prettier 스타일이 표준이 된 상태 — Biome도 Prettier 호환 출력을 약속할 정도
  • 풍부한 파일 포맷 지원 — JSON, YAML, Markdown, GraphQL, MDX 등 Prettier가 가장 넓음

Prettier vs Biome — 2026년의 결정

기준Prettier 4Biome 2
JS/TS 포맷 속도5~15배 가속(Rust 백엔드)25~35배 가속
린터 통합없음 (ESLint 별도)통합
플러그인 생태계거대작음
비표준 파일 지원넓음좁음
단일 설정부분(여전히 .prettierrc + .eslintrc)완전(biome.json)
마이그레이션 비용없음(이미 쓰고 있음)중간 (biome migrate)

한 줄 요약: Prettier는 죽지 않는다 — 생태계가 너무 깊다. Rust 백엔드로 속도 격차를 좁히고, ESLint와의 조합으로 살아남는다. Biome는 "통합의 가치"가 큰 새 프로젝트에서 강하다.


8장 · ESLint v9와 typescript-eslint v8 — flat config 시대

ESLint 진영도 가만히 있지 않았다.

ESLint v9의 변화

  • flat config 표준화.eslintrc.*(legacy) 대신 eslint.config.js(또는 .ts)가 기본. v10에서 legacy는 제거 예정
  • 타입스크립트 ESM 우선 — flat config가 ESM에 자연스러움
  • --inspect-config — 어떤 규칙이 어디서 왜 켜졌는지 시각화
  • CLI 안정화eslint .이 디폴트 동작 (v9부터 인자 없이도 동작)
  • deprecated 규칙 정리 — formatting 관련 규칙은 모두 deprecated (Prettier로 위임)

flat config 예시

// eslint.config.js
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'

export default tseslint.config(
  { ignores: ['dist', 'build', '.next'] },
  js.configs.recommended,
  ...tseslint.configs.recommendedTypeChecked,
  {
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': 'warn',
      '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
    },
  }
)

typescript-eslint v8 — 단일 패키지

v7 이전에는 @typescript-eslint/parser@typescript-eslint/eslint-plugin을 따로 설치해야 했다. v8에서는 typescript-eslint 단일 패키지로 통합. 설정도 더 짧아짐.

npm i -D typescript-eslint

projectService 기능 — 타입 인식 lint의 비용 절감

타입 인식 lint(@typescript-eslint/recommended-type-checked)는 정확하지만 느렸다. v8의 projectService는 TypeScript LSP처럼 동작 — tsconfig를 자동 발견하고, 파일 단위로 타입 정보를 lazy로 로드. 모노레포에서 lint 속도를 2~5배 개선.

ESLint가 살아남는 이유

  • 플러그인 생태계 — eslint-plugin-react-hooks, eslint-plugin-jest, eslint-plugin-jsx-a11y, eslint-plugin-import, eslint-plugin-tailwindcss, eslint-plugin-storybook 등 수백 개
  • 타입 인식 규칙의 깊이no-floating-promises, await-thenable, no-misused-promises처럼 타입 체커 없이 못 잡는 규칙
  • 사용자 정의 규칙의 용이성 — JS로 짠 플러그인을 쉽게 추가
  • 레거시 호환 — flat config로 옮겨도 옛 플러그인을 대부분 수용

ESLint와 Biome의 공존 패턴

많은 팀이 이렇게 한다:

포맷:  Prettier (또는 Biome format)
빠른 린트: oxlint / Biome lint (사전 커밋, 즉시 피드백)
깊은 린트: ESLint (CI, 타입 인식 + 플러그인)

ESLint를 버리는 게 아니라 "빠른 1차 통과 + 깊은 2차 통과"로 분리. Biome+ESLint 같이 도는 게 어색해 보이지만 실제로 잘 작동한다.

한 줄 요약: ESLint는 v9 flat config로 정리됐고, typescript-eslint v8의 projectService로 속도도 개선됐다. 플러그인 생태계 덕에 가까운 시일 안에 죽지 않는다.


9장 · stylelint와 CSS 도구들

CSS 진영도 비슷한 흐름이다.

stylelint v16

  • 여전히 CSS / SCSS / Less 린팅의 디폴트
  • v16에서 flat-config 비슷한 단순화
  • Biome 2의 CSS 린트가 일부 규칙을 흡수 중이지만 stylelint의 전체 커버리지에는 못 미침
  • Tailwind / CSS Modules / postcss 통합은 여전히 stylelint가 강함

CSS 포맷 — 누가 잘하나

도구CSSSCSSLessTailwind 클래스 정렬
Prettier안정안정안정플러그인으로
Biome안정부분(2.x)미지원미지원(직접)
dprint베타 플러그인베타미지원미지원

결정 가이드

  • Tailwind 위주, CSS Modules — Prettier + prettier-plugin-tailwindcss + stylelint (가장 검증)
  • 순수 CSS, 모노레포 속도 중요 — Biome 2 CSS 포맷 + lint, stylelint 보조
  • SCSS 깊게 사용 — Prettier + stylelint (Biome SCSS는 아직 부분)

한 줄 요약: CSS는 아직 Prettier + stylelint 조합이 가장 안전하다. Biome 2가 빠르게 따라잡지만 SCSS·Tailwind 통합은 시간이 더 필요하다.


10장 · 비교 매트릭스 — 한눈에 보는 2026

항목Prettier 4Biome 2dprintRuffoxlintESLint v9stylelint v16
언어JS/TS/CSS/MD/YAML/JSON/Vue/Svelte/AstroJS/TS/JSX/JSON/CSS/GraphQLJS/TS/JSON/MD/TOML/DockerfilePythonJS/TSJS/TSCSS/SCSS/Less
포매터있음있음있음(코어)있음없음없음(deprecated)없음
린터없음있음없음있음있음있음있음
구현 언어JS + Rust(v4)RustRust + WASMRustRustJSJS
속도(상대)1배 (v4 Rust 5~15배)25~35배10~30배50~100배50~80배1배1배
단일 설정부분완전완전(포맷만)완전부분부분부분
플러그인 생태계거대작음중간없음(내장)작음거대거대
자동 수정포맷만포맷 + 일부 lint포맷만광범위일부광범위일부
에디터 통합최고좋음좋음최고좋음최고좋음
도입 비용(기존팀)없음(이미 씀)중간중간낮음낮음(보완)없음(이미 씀)없음
추천 사용처모든 JS/TS, 폭넓은 파일 포맷JS/TS 통합, 속도 우선다중 언어 모노레포Python 거의 모든 곳빠른 1차 lint깊은 lint, 플러그인 활용CSS 모든 곳

한 줄 정리

  • Prettier 4 — 안정성과 생태계의 챔피언, Rust 백엔드로 속도 개선
  • Biome 2 — JS/TS 통합 도구의 선두, 단일 설정의 가치
  • dprint — 다중 언어 포매터, 모노레포에서 강함
  • Ruff — Python 도구 체인의 사실상 표준
  • oxlint — ESLint 빠른 1차 통과, 빌드 도구의 컴포넌트
  • ESLint v9 — flat config로 정리, 플러그인 생태계의 깊이
  • stylelint v16 — CSS 린팅의 디폴트, Biome가 따라잡는 중

11장 · 마이그레이션 — 실제 시나리오 3개

시나리오 A · Prettier + ESLint → Biome 2 (Next.js 모노레포)

# 1. Biome 설치 및 초기화
npm i -D --save-exact @biomejs/biome
npx biome init

# 2. Prettier 설정 마이그레이션
npx biome migrate prettier --write
# 3. ESLint 설정 마이그레이션 (가능한 부분만)
npx biome migrate eslint --write

# 4. 첫 포맷 (큰 diff 한 번)
npx biome format --write .

# 5. 린트 점검
npx biome lint .

# 6. CI / package.json 스크립트 변경
# "lint": "biome lint .",
# "format": "biome format --write ."

현장 보고:

  • 50개 패키지 모노레포에서 CI lint 단계 90초 → 6초
  • 마이그레이션 작업 1.5일 (규칙 차이 메우기)
  • ESLint를 완전히 버리지 않고 type-aware 규칙 일부는 ESLint 보조로 유지
  • Prettier 의견과 Biome 의견의 미세한 차이(긴 표현식 줄바꿈) 때문에 코드 diff 발생, 한 번에 처리

옮기지 말아야 할 신호:

  • eslint-plugin-jest, eslint-plugin-storybook 같은 외부 플러그인 의존 깊음
  • Vue / Svelte / Astro 파일 비중 큼
  • 팀이 Prettier 설정에 만족, 속도 불만 없음

시나리오 B · black + flake8 + isort → Ruff (Django 모노레포)

# pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
extend-select = ["E", "W", "F", "I", "B", "UP", "DTZ", "S", "RUF"]
ignore = ["E501"]

[tool.ruff.format]
quote-style = "double"
# 1. 설치
uv add --dev ruff

# 2. pyproject.toml 설정
# (위와 같이 작성)

# 3. 첫 포맷 (black과 95% 일치, diff 한 번)
ruff format .

# 4. 린트 + 자동 수정
ruff check --fix --unsafe-fixes .

# 5. pre-commit 훅 단순화
# - id: ruff
#   args: [--fix]
# - id: ruff-format
# (이전에는 black, flake8, isort, pyupgrade, autoflake 5개)

현장 보고:

  • 30만 줄 Django 코드베이스에서 pre-commit 90초 → 3초
  • 마이그레이션 0.5일 (black diff 한 번, 규칙 셀렉터 매핑)
  • mypy는 유지(Ruff는 타입 체커 아님)
  • 팀이 만족도 높음, 거의 모든 Python 프로젝트가 동일 패턴 채용

옮기지 말아야 할 신호: 거의 없다. Python에서는 Ruff가 사실상 디폴트.

시나리오 C · 다중 언어 모노레포 → dprint + Biome + Ruff (혼합)

큰 회사의 모노레포에는 TS, Python, Markdown, TOML, Dockerfile, K8s YAML이 같이 산다. 한 도구로 다 못 하면 도구를 분업.

포맷:
  TS / JS / JSON / CSS  → Biome format
  Python                  → Ruff format
  Markdown / TOML / Dockerfile → dprint

린트:
  TS / JS               → Biome lint (빠른) + ESLint (CI, 깊음)
  Python                → Ruff
  CSS                   → stylelint

CI 통합:
  단계 1: dprint check + biome check + ruff check (모두 5~10초)
  단계 2: ESLint --max-warnings 0 (CI에서만, 1~2분)
  단계 3: 타입 체크 (tsc, mypy/ty)

현장 보고: 도구가 3개가 되지만 각 도구가 잘하는 곳에 둠. 사전 커밋 훅은 빠른 도구들만, 깊은 점검은 CI.

한 줄 요약: 마이그레이션은 한 번에 다 옮기지 않아도 된다. 빠른 도구가 잘하는 곳부터 점진적으로, 기존 도구는 깊은 검증으로 남기는 패턴이 가장 안전하다.


12장 · 안티패턴과 함정

빠른 도구를 도입할 때 자주 만나는 함정들.

"Biome 한 줄로 다 해결될 거다"

  • 증상: ESLint 완전 제거, 플러그인 규칙도 한 번에 버림
  • 결과: type-aware 규칙(no-floating-promises)과 플러그인 규칙 부재. 잠재 버그가 검출 안 됨
  • 대신: 빠른 lint = Biome, 깊은 lint = ESLint 보조 유지. 점진적으로 흡수

"Ruff format이 black과 100% 동일"

  • 증상: 큰 PR에서 ruff format을 처음 돌렸을 때 diff 0을 기대
  • 결과: 95% 일치, 엣지 케이스에서 diff 발생. PR이 더러워짐
  • 대신: 마이그레이션 전용 PR 하나를 만들어 한 번에 포맷 적용

"Prettier를 빼면 에디터가 불편해진다"

  • 증상: Biome로 옮긴 후 VS Code에서 format on save가 안 됨
  • 결과: 설정 빠뜨림. 팀원마다 다른 포맷이 commit됨
  • 대신: .vscode/settings.json"editor.defaultFormatter": "biomejs.biome" + Biome 익스텐션 권장

"Rust 도구는 항상 빠르니까 캐싱 필요 없다"

  • 증상: CI에서 cargo cache, plugin cache 무시. 매 빌드마다 다운로드
  • 결과: 도구 자체는 빠르지만 다운로드 단계가 30초~1분 추가
  • 대신: Biome / Ruff 바이너리 캐싱, dprint plugin cache 활용

"린터가 빠르니까 규칙을 다 켜자"

  • 증상: Biome correctness.all, Ruff select = ["ALL"]
  • 결과: false positive 폭발, 팀이 무시하기 시작
  • 대신: recommended에서 시작, 진짜 필요한 규칙만 추가. 모든 규칙은 정책

"사전 커밋 훅에 모든 걸 다 넣자"

  • 증상: husky에 biome + eslint + ruff + tsc + jest 다 넣음
  • 결과: 커밋 1회당 20초~1분. 개발자가 --no-verify로 우회
  • 대신: 사전 커밋은 빠른 것만(포맷, 빠른 lint). 깊은 점검은 CI

"Biome migrate가 완벽할 거다"

  • 증상: biome migrate eslint 한 번 돌리고 끝
  • 결과: 매핑 안 되는 규칙은 무시됨. 잠재 버그 검출 누락
  • 대신: 마이그레이션 후 diff를 사람이 검토, 어떤 규칙이 떨어졌는지 확인

"단일 도구 = 단일 진실"

  • 증상: "Biome가 표준" 하나로 미고, Prettier 출력과 약간 다른 부분에서 팀 충돌
  • 결과: 일부 팀원은 Prettier 인스톨, 다른 팀원은 Biome 인스톨, commit마다 diff
  • 대신: 도구 선택은 팀 합의, .editorconfig + lockfile + 익스텐션 권장으로 강제

"Rust 백엔드 = 무조건 빠름"

  • 증상: 작은 단일 파일에서 Biome 호출이 Prettier보다 느릴 때가 있음(시작 비용)
  • 결과: 마이크로 벤치마크 결과만 보고 결정
  • 대신: 실제 워크플로(모노레포 전체 포맷)에서 측정

"포매터·린터를 통합하면 의견이 충돌하지 않는다"

  • 증상: Biome format과 ESLint stylistic 규칙(중괄호 위치 등)을 동시에 활성
  • 결과: 무한 루프(포맷 → 린트 에러 → 자동 수정 → 포맷 다시)
  • 대신: 포맷 도구를 단일로 정하고, 린트에서는 stylistic 규칙 제거

13장 · 의사결정 트리 — 우리 팀은 무엇을 골라야 하나

시작:
├─ 언어가 Python인가?
│   └─ 예 → Ruff (거의 모든 경우). mypy/ty는 별도
├─ JS/TS 위주인가?
│   │
│   ├─ 새 프로젝트, 단순 규칙, 속도 우선
│   │   └─ Biome 2 단독
│   │
│   ├─ 기존 Prettier+ESLint, 만족, 속도 불만 없음
│   │   └─ 그대로 유지 (Prettier 4 Rust 백엔드 옵션 활성)
│   │
│   ├─ 기존 Prettier+ESLint, 속도 진짜 문제
│   │   ├─ 플러그인 깊게 활용 → Prettier + Biome lint(빠른 1차) + ESLint(깊은)
│   │   └─ 플러그인 적음 → Biome 2로 마이그레이션
│   │
│   ├─ Vue / Svelte / Astro 비중 큼
│   │   └─ Prettier 유지 (Biome 미지원 영역)
│   │
│   └─ 빠른 사전 점검 추가
│       └─ oxlint를 husky 사전 커밋에
├─ 다중 언어 모노레포(TS + Python + 인프라)
│   └─ 도구 분업:
│      - TS → Biome 또는 Prettier
│      - Python → Ruff
│      - 인프라(TOML, Dockerfile, MD) → dprint
├─ CSS / Tailwind 중심
│   └─ Prettier + prettier-plugin-tailwindcss + stylelint
└─ 모노레포 전체 lint가 분 단위
    └─ 1차 빠른 통과 도입:
       - Biome / Ruff / oxlint를 husky 사전 커밋과 CI 첫 단계로
       - 기존 도구는 CI 후반에 깊은 점검 용도로

단순 규칙

  • Python → Ruff
  • 새 JS/TS 프로젝트, 단순 → Biome 2
  • 기존 Prettier+ESLint 잘 돌고 있음 → 유지
  • 모노레포 lint가 진짜 느림 → 빠른 1차 도입
  • 다중 언어 → 도구 분업
  • Tailwind / Vue / Svelte / MDX 비중 큼 → Prettier 유지

에필로그 — 체크리스트, 안티패턴, 다음 글 예고

2026년의 포매터·린터 지형은 5년 전과 다르다. Rust 재작성의 물결로 도구가 10~100배 빨라졌고, "포매터가 린터를 흡수한다"는 통합 흐름이 본격화됐다. Biome 2와 Ruff가 각자의 영역에서 사실상 표준에 가까워졌고, Prettier 4는 Rust 백엔드로 응답했고, ESLint v9는 flat config로 자기 자리를 정리했다.

하지만 "한 도구로 모든 게 끝"이라는 그림은 아직 아니다. 플러그인 생태계, 비표준 파일 지원, type-aware 분석 같은 영역에서 기존 도구가 살아남는다. 우리 팀에 맞는 선택은 — 무엇이 진짜 병목이고, 어떤 트레이드오프를 받아들일지 — 측정에서 시작한다.

도구 선택 체크리스트

  1. 현재 lint·포맷 시간이 진짜로 문제인가? — 30초가 1초가 되어 무엇이 달라지나 측정
  2. 언어별 사정은 다른가? — Python은 Ruff가 거의 결정, JS/TS는 더 복잡
  3. 플러그인 의존이 깊은가? — eslint-plugin-* 깊게 쓰면 ESLint 유지가 안전
  4. 모노레포 / 다중 언어 / 비표준 파일? — dprint 또는 도구 분업 검토
  5. 마이그레이션 한 번에 vs 점진? — 큰 PR은 별도 마이그레이션 PR로
  6. 사전 커밋 vs CI 분리 — 빠른 도구만 사전 커밋에, 깊은 도구는 CI에
  7. type-aware 린트가 필요한가?no-floating-promises 류는 typescript-eslint 유지
  8. 에디터 통합이 되는가? — VS Code / JetBrains 익스텐션 성숙도 확인
  9. CI에서 도구 캐싱이 되는가? — 빠른 도구도 다운로드 단계 캐싱 필요
  10. 팀 합의가 있는가? — 일부만 Biome 쓰면 commit 충돌

안티패턴

안티패턴왜 나쁜가대신
Biome 한 줄로 ESLint 완전 제거type-aware 규칙, 플러그인 부재빠른 lint + 깊은 lint 분리
Ruff format이 black과 100% 같다고 가정95% 일치, 엣지 케이스 diff마이그레이션 PR 별도
모든 규칙을 켜기 (ALL)false positive 폭발, 무시 시작recommended에서 시작, 필요한 것만
사전 커밋에 모든 검사커밋 1분, --no-verify 우회빠른 것만 사전 커밋, 깊은 건 CI
Rust 도구는 캐싱 불필요다운로드 단계가 30초~1분바이너리 / 플러그인 캐싱
Biome migrate 후 검토 없음매핑 안 된 규칙 손실diff 사람 리뷰
단일 도구 강제, 팀 합의 없음일부만 도입, diff 충돌합의 후 lockfile / 익스텐션 권장
마이크로 벤치마크만 보고 결정실제 워크플로와 다름모노레포 전체 시간 측정
포맷·린트 stylistic 충돌무한 루프포맷 단일화, 린트 stylistic 제거
Prettier 4 Rust 백엔드를 즉시 의존베타 단계의 변동성안정화 확인 후 도입

다음 글 예고

다음 글은 **"빌드 도구 2026 — Vite, Turbopack, Rspack, Rolldown, esbuild, swc, oxc의 진짜 위치"**다. 이번 글이 "코드를 어떻게 정돈하는가"의 글이라면, 다음 글은 "코드를 어떻게 묶는가"의 글이다. esbuild의 Go, swc/Turbopack의 Rust, Vite의 esbuild+Rolldown 전환, Rspack의 Rust 재작성, oxc의 컴포넌트화 — 같은 Rust 재작성 패턴이 빌드 도구에서 어떻게 반복되는지, 그리고 무엇을 골라야 하는지를 정리한다.


참고 / References

Formatters and Linters in 2026 — The Real Place of Biome, dprint, Ruff, oxc, Prettier 4, and ESLint v9

Prologue — Why We Are Talking About Formatters Again

There was a time when prettier --write . was the whole story. That was around 2020. Five years later the same command had grown to 30 seconds in a monorepo and two minutes on CI. eslint --max-warnings 0 was slower still, and at some point we were seriously debating whether to remove lint from the pre-commit hook.

The Python world had a parallel pain. The classic combo of black, flake8, isort, and pylint was stable but slow. A full pass on a large repo took minutes, and four config files drifted apart.

Then a wave of Rust tooling arrived with a different answer: if the tool is genuinely 100 times faster, every other workflow assumption changes. Ruff proved it in Python in 2022, and Biome (formerly Rome) took the same path in JS and TS. dprint, oxc, ty (a Rust type checker), and uv (a Rust package manager) followed — a sustained pattern of Rust rewrites led by Astral, the Biome team, and small projects like Boshen's oxc.

In 2026, Prettier 4 officially adopts a Rust backend path, ESLint v9's flat config has stabilized, and typescript-eslint v8 collapses into a single package. Biome 2 ships multi-file analysis and import sorting as first-class features, and Ruff is effectively the Python linting default.

This post maps that terrain. Which tool wins where, how far the "rewrite in Rust and everything gets better" claim actually holds, and what your team should pick. Measured trade-offs, not marketing copy.

Headline: tools got fast and unified. But "Biome solves everything" is still not the picture. The choice comes down to which trade-offs you accept.


1. The Map — A 2026 Overview of Formatters and Linters

Start with the big picture: which tools own which language territory in 2026.

Language / areaTraditional stack2026 defaultNext-gen contender
JS / TSPrettier + ESLintPrettier + ESLint v9, Biome 2oxc, dprint
Pythonblack + flake8 + isort + pylintRuff (format + lint)ty (type checker)
Rustrustfmt + clippyrustfmt + clippyalready Rust
Gogofmt + golangci-lintunchangednone
CSS / SCSSPrettier + stylelintPrettier + stylelint, Biome 2dprint, oxc
MarkdownPrettier, remark-lintPrettier, dprintnone
TOML / YAML / JSONPrettierdprint, taplo, yamlfmtnone
Multi-language monorepoPrettier + ESLintdprint (unifying formatter), Biomenone

One-line takes by category

  • Formatter: decides shape — whitespace, line breaks, quotes, semicolons. Opinionated and enforces. Prettier, Biome format, dprint, rustfmt, gofmt, Ruff format, black
  • Linter: catches semantic issues — unused variables, likely bugs, anti-patterns. ESLint, Biome lint, Ruff lint, clippy, golangci-lint, stylelint, pylint
  • Type checker: catches type errors. tsc, mypy, pyright, ty (Astral), Flow
  • Integrated formatter + linter: a single binary that does both. Biome and Ruff are the canonical examples

The protagonist of this post is the third category — Rust-written next-gen integrated tools. Where they replace incumbents, where they only supplement, and where they cannot yet go.

Headline: situations differ by language. Python is effectively decided by Ruff. JS and TS still have a live Biome vs Prettier+ESLint debate. Monorepos opened a new lane for dprint.


2. The Rust Rewrite Pattern — Why 10 to 100 Times Faster

Start with the structural reason — not marketing.

Numbers you can measure

Biome's own benchmarks show 25 to 35 times the throughput of Prettier on single-file formatting, with a wider gap on big repositories. Ruff is regularly reported as roughly 100 times faster than flake8 on equivalent rule sets, turning minute-scale checks into single-digit seconds on medium-to-large Python repos.

Patterns from real-world reports:

  • Prettier on a monorepo, 30 seconds; Biome, 1 to 2 seconds
  • flake8 + black on a monorepo, 90 seconds; Ruff, 2 to 3 seconds
  • ESLint flat config on a monorepo, 45 seconds; Biome, 3 to 5 seconds (different rule coverage, more on this below)
  • Prettier on a single file, 300 ms; dprint or Biome, 5 to 15 ms

Why the gap is this large

  1. Language baseline — JS tools run as interpreted code on V8's JIT; Rust tools are native binaries. That alone buys 3 to 10 times
  2. Single-pass parsing — legacy tools do lex, parse, AST transform in multiple passes. Biome, oxc, and Ruff build one lossless concrete syntax tree (CST) and reuse it for every analysis
  3. Real parallelism — Rust leans into data parallelism so files actually run on multiple cores. JS tools are single-threaded; worker pools pay serialization overhead
  4. Memory layout — CST nodes are stored in cache-friendly arrays (rowan, oxc-resolver). Fewer allocations
  5. Startup cost — a Rust binary starts in 1 to 5 ms; Node in 50 to 200 ms. Repeated CLI invocations compound this

Second-order effects of being fast

  • Pre-commit hooks become viable again — 30 seconds was forbidden, 1 second is invisible
  • Editor integration becomes live — lint on every keystroke is realistic
  • CI caching matters less — already fast enough not to need an optimization tier
  • Full-monorepo lint becomes routine — minutes meant CI-only, seconds means local-too

Headline: the second-order workflow shift matters more than the raw speed. "Too slow to be worth it" was an unspoken constraint that just got removed.


3. Biome 2 — The Frontrunner of Formatter and Linter Fusion

Biome is the most ambitious integration. The name began as Rome (2020, Sebastian McKenzie — the author of Babel), the project paused due to funding, and the community fork shipped as Biome in 2023. Biome 1.0 in 2024, 2.0 in 2025.

What Biome 2 promises

  • JS / TS / JSX / TSX / JSON / CSS / GraphQL formatting + linting in one binary
  • A single config file biome.json — Prettier and ESLint configs collapsed
  • Type-aware linting — overlaps with typescript-eslint's type-aware rules (v2 adds multi-file analysis)
  • Import sorting — supersedes parts of eslint-plugin-import
  • Diff format — format only the changed portion, useful for large PRs in CI
  • Migration toolingbiome migrate prettier, biome migrate eslint

Sample config

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
  "files": { "ignoreUnknown": true },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "javascript": {
    "formatter": { "quoteStyle": "single", "semicolons": "asNeeded" }
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": { "noUnusedVariables": "error" },
      "style": { "useImportType": "warn" },
      "suspicious": { "noExplicitAny": "warn" }
    }
  },
  "assist": {
    "enabled": true,
    "actions": { "source": { "organizeImports": "on" } }
  }
}

Where Biome shines

  • JS/TS focused, simple rule sets — teams that find 90% rule coverage sufficient
  • Teams that value a single config — anyone tired of the Prettier+ESLint configuration surface
  • CI speed is a real bottleneck — monorepo lint that ran in minutes
  • Local editor integration speed — the VS Code extension responds instantly

Where Biome falls short

  • No ESLint plugin ecosystem — eslint-plugin-react-hooks, eslint-plugin-jest, eslint-plugin-tailwindcss, eslint-plugin-import are not directly usable. Biome absorbs some of them into the core, but 1:1 parity is hard
  • Non-standard file formats are missing.vue and .svelte are not officially supported (some community plugins exist)
  • No Markdown / YAML / TOML formatting — territory Prettier covers
  • Limited type-aware lint — does not fully match typescript-eslint's rule depth
  • Migration cost — papering over rule differences in a large repo takes days

Decision matrix

Team situationBiome fit
Greenfield JS/TS, simple rulesvery high
Monorepo, CI speed matters, no Vue/Sveltehigh
Existing Prettier+ESLint, satisfiedweak motivation to switch
Heavy eslint-plugin-* usagenot yet
Big share of Vue / Svelte / Astro / MDXnot recommended

Headline: Biome is the most compelling unified JS/TS tool today. Plugin ecosystem and non-standard file support are still the gaps before it can fully replace Prettier+ESLint.


4. dprint — The Plugin-Driven Multi-Language Formatter

dprint is a different shape. Tiny core, language-specific plugins. It formats JS/TS, JSON, Markdown, TOML, Dockerfile, and SQL out of one tool.

What sets dprint apart

  • A WASM plugin system — language support lives in plugins; the core stays small and fast
  • Designed for multi-language monorepos — covers files Prettier never handled (TOML, Dockerfile)
  • Rust core with a Rust / WASM plugin SDK
  • Pure formatter — no linting; does one thing well

Sample config

{
  "lineWidth": 100,
  "indentWidth": 2,
  "useTabs": false,
  "typescript": {
    "quoteStyle": "preferSingle",
    "semiColons": "asi"
  },
  "json": {
    "lineWidth": 200
  },
  "markdown": {
    "textWrap": "always"
  },
  "toml": {},
  "dockerfile": {},
  "plugins": [
    "https://plugins.dprint.dev/typescript-0.93.0.wasm",
    "https://plugins.dprint.dev/json-0.20.0.wasm",
    "https://plugins.dprint.dev/markdown-0.18.0.wasm",
    "https://plugins.dprint.dev/toml-0.7.1.wasm",
    "https://plugins.dprint.dev/dockerfile-0.3.4.wasm"
  ],
  "includes": ["**/*.{ts,tsx,js,jsx,json,md,toml,Dockerfile}"],
  "excludes": ["**/node_modules", "**/dist"]
}

Where dprint shines

  • Multi-language monorepos — TS + JSON + Markdown + TOML + Dockerfile in one tool
  • Pure-formatter value — no need to bundle linting, just a faster Prettier replacement
  • Plugin isolation — each language is a WASM module; no dependency conflicts
  • Version pinning conveniencedprint config update refreshes plugin versions in one step

Where dprint falls short

  • No linting — pair it with ESLint, Biome lint, or Ruff
  • Rule coverage for JS/TS — slightly thinner than Prettier; opinions are stronger
  • Editor maturity — the VS Code extension works but is not as smooth as Prettier's
  • CSS and GraphQL plugins — some are beta or community-maintained

Who actually uses it

  • Deno-style codebases (dprint plays well with Deno)
  • Infrastructure repos (IaC, Docker, Kubernetes, Terraform)
  • Polyglot monorepos (TS + Python + infra code together)

Headline: dprint aims to be the one formatter for every kind of text. No linting, formatting only, and it shines in a truly polyglot monorepo.


5. Ruff — The Rust Tool That Took Python

Ruff is the most overwhelming success story. Charlie Marsh (Astral) shipped the first release in 2022; by 2026 it is effectively number one on PyPI download stats for Python developer tooling. Django, Pandas, FastAPI, Pydantic, Apache Airflow, scikit-learn — all major projects adopt Ruff.

What Ruff promises

  • Replacement for flake8, isort, pydocstyle, pyupgrade, bandit, autoflake, black — 7 to 10 tools collapse to 1
  • Over 800 lint rules — most of flake8 plus its major plugins
  • Built-in formatterruff format is black-compatible (it preserves black's strong opinions almost identically)
  • Import sorting — supersedes isort
  • Auto-fixruff check --fix applies safe corrections automatically
  • Single config — one [tool.ruff] section in pyproject.toml

Sample config

[tool.ruff]
line-length = 100
target-version = "py312"
extend-exclude = ["migrations", ".venv", "build"]

[tool.ruff.lint]
select = [
  "E", "W",      # pycodestyle
  "F",           # pyflakes
  "I",           # isort
  "N",           # pep8-naming
  "UP",          # pyupgrade
  "B",           # flake8-bugbear
  "C4",          # flake8-comprehensions
  "DTZ",         # flake8-datetimez
  "S",           # flake8-bandit (security)
  "RUF",         # ruff-specific
]
ignore = ["E501"]  # line-too-long handled by formatter
fixable = ["ALL"]
unfixable = []

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"]  # allow assert in tests
"__init__.py" = ["F401"]  # allow unused imports

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true

Where Ruff shines — nearly everywhere

  • Speed — flake8+pylint at 90 seconds collapse to Ruff at 2 seconds
  • Single tool — pre-commit configuration shrinks from 7 entries to 1
  • Auto-fix breadth — what flake8 only reported, Ruff also fixes
  • No CI caching needed — already fast
  • flake8-rule-selector compatible — low migration cost

Where Ruff has limits and differences

  • Not a mypy replacement — type checking is separate. Astral's ty (Rust type checker) is in alpha / beta in 2026
  • Not 100% identical to black — 95% match; edge cases (long strings, complex expressions) differ. Running ruff format once will produce a diff — plan for a one-shot reformatting PR
  • Some deep pylint analyses are weaktoo-many-locals, cyclomatic complexity rules are limited
  • No plugin system — every rule lives in the core; custom rules are hard to add

Astral's bigger picture — a Rust rewrite of the Python stack

Charlie Marsh's ambition does not stop at Ruff. Astral is rewriting the entire Python developer tooling chain in Rust.

ToolReplacesStatus (2026)
Ruffflake8, isort, black, pydocstyle, ...stable, broadly adopted
uvpip, virtualenv, pip-tools, pipx, parts of poetrystable, rapidly spreading
tymypy, parts of pyrightalpha / beta
ryepoetry, hatch conceptuallybeing absorbed into uv

The pattern: once one tool gets fast, the whole workflow reorganizes around it. The same pattern is repeated by Biome and oxc in JS.

Headline: Ruff is the de facto default of the Python developer tooling chain. The reasons are simple — fast, integrated, low migration cost.


6. oxc — The Rust JavaScript Toolchain Family

oxc takes a different shape of ambition. Led by Boshen, the project rewrites the entire JavaScript toolchain in Rust — parser, resolver, transformer, linter, formatter, minifier. Where Biome is a "single-user tool," oxc is closer to "a component library other tools consume."

What oxc ships

ComponentRoleCompares to
oxc-parserJS/TS parseracorn, esprima, swc parser
oxc-resolvermodule resolutionenhanced-resolve
oxc-transformerJS/TS transformBabel, swc
oxlintlinterESLint
oxc-prettierformatter (Prettier Rust port)Prettier
oxc-minifierminifierterser, swc, esbuild

oxlint — the Rust linter aimed at ESLint

oxlint reimplements ESLint core rules and parts of major plugins in Rust. As of 2026, oxlint supports parts of an ESLint v9 compatibility mode and is positioned as a fast pre-flight check.

# Fast lint with oxlint, deep lint with ESLint
oxlint --deny-warnings src/
# ESLint-compatible reporting
oxlint --rules-include=react-hooks src/

Where oxc shines

  • Build tool / framework authors — Rspack, Rolldown, and others consume oxc internally. Fast component library
  • Pre-flight lint — Lefthook or Husky pre-commit hooks run oxlint for 70% rule coverage in 0.5 seconds, leaving ESLint for the deep pass
  • CI first stage — fail fast. Pass oxlint, then run ESLint deeper checks
  • Prettier-compatible formatter — part of the path Prettier 4's Rust backend treads

Where oxc has limits

  • Plugin system immature — ESLint plugins cannot be reused directly
  • Lint rule coverage partial — no 1:1 mapping to the full ESLint rule set
  • Hard to use standalone — not "this is all you need" like Biome, more of a complement
  • The formatter is closer to a Prettier 4 integration path — standalone use cases are few

Biome vs oxc — what differs

AxisBiomeoxc
Goalunified user toolcomponent library + tool family
Single configyes (biome.json)per-tool
User experience"replace Prettier+ESLint""speed up the ESLint workflow"
Pluginsmostly built-in(planned) WASM plugins
Who adoptsapplication teamsbuild tool / framework authors

Headline: Biome targets users, oxc targets tool builders. From a developer's seat Biome is intuitive; from a build-tool author's seat oxc is the practical pick. They live on different layers, not in direct competition.


7. Prettier 4 — How the Reigning Tool Responded

Prettier has been the de facto JS/TS formatter since 2017. Strong opinions, almost no configuration — that is the core philosophy. As a result, nearly every project adopted it.

But the speed problem accumulated. Through version 3, Prettier was pure JS on Node. As the gap to native tools widened, the Prettier team made a Rust backend path official in 2025.

Headline changes in Prettier 4

  • Rust-backed parser and formatter path — the JS/TS core gets an adapter that can run on top of oxc or swc based Rust backends
  • Compatibility first — bit-identical output to Prettier 3 is the goal (95%+ match). Reaching 100% is incremental
  • Plugin API stabilization — Markdown, YAML, GraphQL, Tailwind plugins keep working
  • Speed gains — 5 to 15 times faster with the Rust backend enabled (not yet matching Biome's raw throughput but meaningful)
  • Same installation modelnpm install prettier and you are done

Sample config

{
  "experimentalRustBackend": true,
  "printWidth": 100,
  "singleQuote": true,
  "semi": false,
  "trailingComma": "all",
  "plugins": [
    "prettier-plugin-tailwindcss",
    "prettier-plugin-organize-imports"
  ]
}

Why Prettier survives

  • Ecosystem inertia — hundreds of plugins (Tailwind ordering, import sorting, Astro / Svelte / Vue support)
  • Editor integration maturity — "format on save" in VS Code remains the most reliable
  • Opinion of opinions — Prettier style is the style — even Biome promises Prettier-compatible output
  • Broad file format support — JSON, YAML, Markdown, GraphQL, MDX

Prettier vs Biome — choosing in 2026

AxisPrettier 4Biome 2
JS/TS format speed5 to 15 times (Rust backend)25 to 35 times
Lint integrationnone (ESLint separate)integrated
Plugin ecosystemhugesmall
Non-standard file supportbroadnarrow
Single configpartial (.prettierrc + .eslintrc still)full (biome.json)
Migration costnone (already in use)medium (biome migrate)

Headline: Prettier is not going away — the ecosystem is too deep. The Rust backend narrows the speed gap, and pairing with ESLint keeps it competitive. Biome shines when "integration value" matters on a new project.


8. ESLint v9 and typescript-eslint v8 — The Flat Config Era

The ESLint camp did not stand still.

What ESLint v9 changes

  • flat config is the standardeslint.config.js (or .ts) replaces .eslintrc.* (legacy). Legacy goes away in v10
  • First-class TypeScript ESM — flat config maps naturally to ESM
  • --inspect-config — visualize which rule is enabled where and why
  • CLI stabilizationeslint . is the default (no arguments required from v9)
  • Deprecated formatting rules — every formatting-related rule is deprecated and delegated to Prettier or another formatter

Sample flat config

// eslint.config.js
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'

export default tseslint.config(
  { ignores: ['dist', 'build', '.next'] },
  js.configs.recommended,
  ...tseslint.configs.recommendedTypeChecked,
  {
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': 'warn',
      '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
    },
  }
)

typescript-eslint v8 — a single package

Before v7 you installed @typescript-eslint/parser and @typescript-eslint/eslint-plugin separately. v8 collapses to a single typescript-eslint package. Config gets shorter, too.

npm i -D typescript-eslint

projectService — cutting the cost of type-aware lint

Type-aware lint (@typescript-eslint/recommended-type-checked) is precise but slow. v8's projectService works like the TypeScript LSP — discovers tsconfig automatically and lazy-loads type information per file. Lint speed improves 2 to 5 times on monorepos.

Why ESLint survives

  • Plugin ecosystem — eslint-plugin-react-hooks, eslint-plugin-jest, eslint-plugin-jsx-a11y, eslint-plugin-import, eslint-plugin-tailwindcss, eslint-plugin-storybook, hundreds more
  • Depth of type-aware rulesno-floating-promises, await-thenable, no-misused-promises need the type checker
  • Ease of authoring custom rules — write a plugin in plain JS
  • Legacy compatibility — flat config still accepts most legacy plugins

The pattern of coexisting Biome and ESLint

Many teams arrive at this:

Format:           Prettier (or Biome format)
Fast lint:        oxlint / Biome lint  (pre-commit, immediate feedback)
Deep lint:        ESLint                (CI, type-aware + plugins)

ESLint is not being replaced — the workflow is split into a fast first pass and a deep second pass. Biome and ESLint running side by side may look odd, but it works in practice.

Headline: ESLint v9 settled with flat config, and typescript-eslint v8's projectService closes the speed gap. The plugin ecosystem keeps it alive for the foreseeable future.


9. stylelint and the CSS Tooling Picture

CSS follows a similar pattern.

stylelint v16

  • Remains the default for linting CSS / SCSS / Less
  • v16 brings flat-config-like simplification
  • Biome 2's CSS lint absorbs some rules, but does not match stylelint's coverage
  • Tailwind, CSS Modules, and PostCSS integration are still stronger in stylelint

Who formats CSS well

ToolCSSSCSSLessTailwind class sorting
Prettierstablestablestablevia plugin
Biomestablepartial (2.x)unsupportedunsupported (directly)
dprintbeta pluginbetaunsupportedunsupported

Decision guide

  • Heavy Tailwind, CSS Modules — Prettier + prettier-plugin-tailwindcss + stylelint (the most battle-tested)
  • Plain CSS, monorepo speed matters — Biome 2 CSS format + lint, stylelint as a complement
  • Heavy SCSS usage — Prettier + stylelint (Biome's SCSS is still partial)

Headline: Prettier + stylelint remains the safest combination for CSS. Biome 2 is catching up fast, but SCSS and Tailwind integration need more time.


10. The Comparison Matrix — One View of 2026

AxisPrettier 4Biome 2dprintRuffoxlintESLint v9stylelint v16
LanguagesJS/TS/CSS/MD/YAML/JSON/Vue/Svelte/AstroJS/TS/JSX/JSON/CSS/GraphQLJS/TS/JSON/MD/TOML/DockerfilePythonJS/TSJS/TSCSS/SCSS/Less
Formatteryesyesyes (core)yesnono (deprecated)no
Linternoyesnoyesyesyesyes
ImplementationJS + Rust (v4)RustRust + WASMRustRustJSJS
Relative speed1x (v4 Rust 5 to 15x)25 to 35x10 to 30x50 to 100x50 to 80x1x1x
Single configpartialfullfull (format only)fullpartialpartialpartial
Plugin ecosystemhugesmallmediumnone (built-in)smallhugehuge
Auto-fixformat onlyformat + some lintformat onlybroadpartialbroadpartial
Editor integrationbestgoodgoodbestgoodbestgood
Adoption cost (existing team)none (already in use)mediummediumlowlow (complement)none (already in use)none
Recommended useall JS/TS, broad file formatsJS/TS integrated, speed firstmulti-language monorepoalmost all Pythonfast first-pass lintdeep lint, plugin ecosystemall CSS

One-line summaries

  • Prettier 4 — stability and ecosystem champion, Rust backend narrows the speed gap
  • Biome 2 — the integrated JS/TS frontrunner, the value of one config
  • dprint — multi-language formatter, shines in monorepos
  • Ruff — de facto standard for the Python toolchain
  • oxlint — fast first-pass for ESLint, component piece for build tools
  • ESLint v9 — flat config tidy-up, plugin depth keeps it relevant
  • stylelint v16 — CSS lint default, Biome catching up

11. Migrations — Three Real Scenarios

Scenario A · Prettier + ESLint to Biome 2 (Next.js monorepo)

# 1. Install and initialize Biome
npm i -D --save-exact @biomejs/biome
npx biome init

# 2. Migrate Prettier config
npx biome migrate prettier --write
# 3. Migrate ESLint config (where mappable)
npx biome migrate eslint --write

# 4. First formatting pass (one large diff)
npx biome format --write .

# 5. Lint pass
npx biome lint .

# 6. Update CI / package.json scripts
# "lint": "biome lint .",
# "format": "biome format --write ."

Field report:

  • 50-package monorepo: CI lint stage dropped from 90 seconds to 6 seconds
  • Migration effort: ~1.5 days (papering over rule differences)
  • Did not drop ESLint entirely; some type-aware rules stayed as a complement
  • Minor differences between Prettier and Biome opinions (long expression wrapping) produced a diff that was handled in one PR

Signs you should not migrate:

  • Deep dependence on eslint-plugin-jest, eslint-plugin-storybook, and similar external plugins
  • Significant share of Vue / Svelte / Astro files
  • Team is happy with Prettier; no real speed complaint

Scenario B · black + flake8 + isort to Ruff (Django monorepo)

# pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
extend-select = ["E", "W", "F", "I", "B", "UP", "DTZ", "S", "RUF"]
ignore = ["E501"]

[tool.ruff.format]
quote-style = "double"
# 1. Install
uv add --dev ruff

# 2. Configure pyproject.toml (as above)

# 3. First format pass (95% match with black, one diff)
ruff format .

# 4. Lint + auto-fix
ruff check --fix --unsafe-fixes .

# 5. Simplified pre-commit
# - id: ruff
#   args: [--fix]
# - id: ruff-format
# (previously: black, flake8, isort, pyupgrade, autoflake — 5 entries)

Field report:

  • 300k-line Django codebase: pre-commit dropped from 90 seconds to 3 seconds
  • Migration effort: ~0.5 days (one black-style diff, rule-selector mapping)
  • mypy was kept (Ruff is not a type checker)
  • High team satisfaction; nearly every Python project converges on this same pattern

Signs you should not migrate: very few. Ruff is effectively the Python default.

Scenario C · Polyglot monorepo with dprint + Biome + Ruff (split workflow)

A big-company monorepo lives with TS, Python, Markdown, TOML, Dockerfile, and Kubernetes YAML side by side. No single tool covers all of that, so split the work.

Format:
  TS / JS / JSON / CSS    -> Biome format
  Python                   -> Ruff format
  Markdown / TOML / Docker -> dprint

Lint:
  TS / JS                 -> Biome lint (fast) + ESLint (deep, CI)
  Python                  -> Ruff
  CSS                     -> stylelint

CI stages:
  Stage 1: dprint check + biome check + ruff check  (all 5 to 10 seconds)
  Stage 2: ESLint --max-warnings 0                   (CI only, 1 to 2 minutes)
  Stage 3: type checking (tsc, mypy / ty)

Field report: three tools instead of one, but each tool is placed where it shines. Pre-commit runs only the fast tools; the deep checks live on CI.

Headline: migrations do not have to be all-or-nothing. Adopt the fast tool where it wins and keep the incumbent for deep verification. This staged path is the safest.


12. Anti-Patterns and Pitfalls

Common traps when adopting fast tooling.

"Biome alone will solve everything"

  • Symptom: ESLint is removed entirely, plugin rules dropped overnight
  • Result: type-aware rules (no-floating-promises) and plugin rules disappear. Latent bugs slip through
  • Instead: split into fast lint (Biome) and deep lint (ESLint). Migrate gradually

"Ruff format equals black 100%"

  • Symptom: assume zero diff on the first ruff format of a big PR
  • Result: 95% match, edge-case differences. The PR gets noisy
  • Instead: do a dedicated reformat PR up front

"Editing gets worse without Prettier"

  • Symptom: VS Code stops formatting on save after switching to Biome
  • Result: settings missed. Different formats land on commits
  • Instead: set "editor.defaultFormatter": "biomejs.biome" in .vscode/settings.json and require the Biome extension

"Rust tools are fast — no caching needed"

  • Symptom: CI ignores cargo cache and plugin cache; downloads on every build
  • Result: tool itself is fast, but 30 seconds to 1 minute of download overhead
  • Instead: cache Biome / Ruff binaries and dprint plugins

"Linter is fast — turn every rule on"

  • Symptom: Biome correctness.all, Ruff select = ["ALL"]
  • Result: false positives explode, the team starts ignoring lint
  • Instead: start from recommended, add only what your codebase needs. Every rule is a policy

"Pre-commit should run everything"

  • Symptom: husky runs biome + eslint + ruff + tsc + jest
  • Result: 20 seconds to 1 minute per commit. Developers route around with --no-verify
  • Instead: pre-commit runs only fast checks; deep checks belong on CI

"Biome migrate will be perfect"

  • Symptom: run biome migrate eslint once and call it done
  • Result: rules that did not map silently disappeared. Latent bugs missed
  • Instead: review the diff post-migration; record which rules dropped

"Single tool equals single source of truth"

  • Symptom: the team declares "Biome is the standard" but Prettier output still differs slightly
  • Result: some team members install Prettier, others Biome, every commit produces a diff
  • Instead: agree on a tool, then enforce via .editorconfig, lockfile, and required editor extension

"Rust backend means uniformly faster"

  • Symptom: micro-benchmarks of single small files where Biome is sometimes slower than Prettier (startup cost)
  • Result: decisions based on the wrong measurement
  • Instead: measure the workflow that matters — full monorepo format times

"Integrating formatter + linter avoids conflicts"

  • Symptom: Biome format and ESLint stylistic rules (brace placement, etc.) both enabled
  • Result: infinite loop — format, then lint flags, then auto-fix changes format again
  • Instead: pick one tool as the formatter; remove stylistic rules from the linter

13. The Decision Tree — What Should Your Team Pick

Start:
|
+- Language is Python?
|   +- Yes -> Ruff (almost always). Keep mypy / ty separate.
|
+- Primarily JS/TS?
|   |
|   +- Greenfield, simple rules, speed-first
|   |   +- Biome 2 standalone
|   |
|   +- Existing Prettier+ESLint, satisfied, no speed pain
|   |   +- Keep it (consider enabling Prettier 4 Rust backend)
|   |
|   +- Existing Prettier+ESLint, real speed pain
|   |   +- Heavy plugin usage -> Prettier + Biome lint (fast first) + ESLint (deep)
|   |   +- Light plugin usage -> Migrate to Biome 2
|   |
|   +- Heavy Vue / Svelte / Astro
|   |   +- Stay on Prettier (Biome cannot cover)
|   |
|   +- Need a faster pre-commit
|       +- Add oxlint to the husky pre-commit
|
+- Polyglot monorepo (TS + Python + infra)
|   +- Split tools:
|      - TS -> Biome or Prettier
|      - Python -> Ruff
|      - infra (TOML, Dockerfile, MD) -> dprint
|
+- CSS / Tailwind heavy
|   +- Prettier + prettier-plugin-tailwindcss + stylelint
|
+- Monorepo-wide lint takes minutes
    +- Add a fast first pass:
       - Biome / Ruff / oxlint as the husky pre-commit and CI stage 1
       - Keep the existing tool for the deep CI pass

Simple rules

  • Python -> Ruff
  • Greenfield JS/TS, simple -> Biome 2
  • Prettier+ESLint already working -> keep it
  • Real monorepo lint pain -> add a fast first pass
  • Polyglot -> split tools
  • Heavy Tailwind / Vue / Svelte / MDX -> stay on Prettier

Epilogue — Checklist, Anti-Patterns, Next Post

The 2026 picture is different. The Rust rewrite wave bought 10 to 100 times speed, and the "formatter absorbs the linter" pattern finally materialized. Biome 2 and Ruff sit at near-standard status in their domains, Prettier 4 replied with a Rust backend, and ESLint v9 cleaned house with flat config.

But the picture is still not "one tool does it all." Plugin ecosystems, non-standard file formats, and type-aware analysis keep incumbents alive. The right pick for your team starts from measurement — what is the real bottleneck, and which trade-offs you accept.

Tool selection checklist

  1. Is your lint and format time actually a problem? — measure what changes when 30 seconds becomes 1 second
  2. Are the per-language stories different? — Python is mostly settled; JS/TS is not
  3. Do you depend deeply on plugins? — heavy eslint-plugin-* use keeps ESLint
  4. Monorepo, polyglot, non-standard files? — consider dprint or split tools
  5. Migrate all at once or incrementally? — large migrations belong in a dedicated PR
  6. Pre-commit versus CI separation — fast tools belong on pre-commit, deep ones on CI
  7. Do you need type-aware lint? — keep typescript-eslint for no-floating-promises and friends
  8. Does editor integration actually work? — verify VS Code / JetBrains extension maturity
  9. Do you cache tools on CI? — even fast tools have a download step worth caching
  10. Is there team agreement? — partial adoption produces conflicting commits

Anti-pattern recap

Anti-patternWhy it bitesInstead
Dropping ESLint entirely for Biometype-aware rules and plugins vanishsplit fast lint and deep lint
Assuming Ruff format equals black 100%95% match, edge-case diffsdedicated reformat PR
Enabling every rule (ALL)false-positive explosion, team ignoresstart from recommended, add what you need
Pre-commit runs everythingminute-scale commits, --no-verify workaroundsfast on pre-commit, deep on CI
Skipping caching for Rust tools30 to 60 seconds of download costcache binaries and plugins
No diff review post-migrationlost rules unnoticedhuman-review the migration diff
Single tool without team buy-inpartial adoption, diff conflictsagreement first, enforce via tooling
Optimizing for micro-benchmarksnot your actual workflowmeasure full monorepo timings
Format and lint stylistic clashinfinite loopone formatter, drop stylistic lint rules
Treating Prettier 4 Rust backend as production-ready immediatelybeta volatilityconfirm stability before adopting

Next post

Next: "Build Tools in 2026 — The Real Place of Vite, Turbopack, Rspack, Rolldown, esbuild, swc, and oxc". If this post was about how we tidy code, the next is about how we bundle it. esbuild's Go, swc and Turbopack's Rust, Vite's transition to esbuild plus Rolldown, Rspack's Rust rewrite, and oxc's componentization — the same Rust rewrite pattern, applied to bundlers.


References