Lintowanie i formatowanie kodu to jeden z tych obszarów, gdzie każdy zespół kiedyś traci czas, który mógłby poświęcić na właściwą pracę. ESLint konfiguruje się przez godziny. Prettier kłóci się z ESLint przez eslint-config-prettier. Pre-commit hooki trwają osiem sekund. CI blokuje PR przez minuty. I każdy nowy developer musi rozgryźć, dlaczego w projekcie jest siedem różnych plików konfiguracyjnych.
Biome to odpowiedź na ten problem — jeden plik konfiguracyjny, jedna komenda, prędkość liczona w milisekundach. W tym artykule pokazuję, jak przeprowadzić migrację w istniejącym projekcie TypeScript, na co uważać i co konkretnie możesz zyskać.
Dlaczego ESLint + Prettier staje się problemem
Na początku projektu konfiguracja ESLint i Prettier wydaje się prosta. Instalujesz kilka pakietów, kopiujesz config z poprzedniego projektu i działa. Problem zaczyna się, gdy projekt rośnie.
Typowy package.json po roku wytwarzania kodu wygląda tak: eslint, prettier, eslint-config-prettier, eslint-plugin-react, eslint-plugin-import, @typescript-eslint/eslint-plugin, @typescript-eslint/parser, eslint-plugin-jsx-a11y, eslint-plugin-unicorn — i to nie jest kompletna lista. Każdy z tych pakietów ma własne zależności, własny cykl wydań i własny sposób konfiguracji. Gdy wychodzi nowa wersja TypeScript albo React, przez kilka tygodni czekasz, aż wszystkie pluginy ją obsłużą.
Drugi problem to prędkość. ESLint działa w jednym wątku i nie był projektowany z myślą o dużych monorepo. W kodzie z realnej produkcji lintowanie 200,000 linii kodu bez automatycznych poprawek zajmowało prawie siedem minut. Pre-commit hooki trwające powyżej pięciu sekund to efektywnie hooki, które developerzy wyłączają — bo nikt nie będzie czekał osiem sekund przy każdym commicie.
Trzeci problem to konfiguracja. Masz .eslintrc.json, .eslintignore, .prettierrc, .prettierignore. Jeśli pracujesz w monorepo, tych plików jest kilkanaście. Onboarding nowego developera to nie tylko npm install — to półgodzinna rozmowa o tym, dlaczego konfiguracja wygląda tak jak wygląda.
Porównanie stosu narzędzi: ESLint+Prettier (wiele pakietów, wiele plików konfiguracyjnych) vs Biome (jeden pakiet, jeden plik biome.json)
Czym jest Biome i co oferuje
Biome to toolchain do analizy i formatowania kodu, napisany w Rust. Łączy formatter (zamiennik Prettier) i linter (zamiennik ESLint) w jednym binarnym pliku, z jedną konfiguracją w biome.json.
Projekt zaczął się jako fork porzuconego Rome Tools. Od 2023 roku działa jako niezależny projekt. Wersja 1.0 pojawiła się w sierpniu 2023, wersja 2.0 w 2025, a aktualna — 2.4 — pochodzi z początku 2026. Na GitHubie projekt ma ponad 24 tysiące gwiazdek i wygrał nagrodę "Productivity Booster" na OS Awards 2024.
Co Biome robi lepiej niż ESLint+Prettier:
Prędkość. Biome jest napisany w Rust, działa wielowątkowo i nie wczytuje setek modułów Node.js przy każdym uruchomieniu. Oficjalny benchmark: 171,000 linii kodu w ponad 2,000 plikach — Biome jest ~35x szybszy od Prettier przy formatowaniu. W praktyce, na monorepo z prawdziwego projektu, lintowanie z automatycznym naprawianiem błędów skrócono z 7 minut do 1.6 sekundy. To 250-krotne przyspieszenie.
Jeden plik konfiguracyjny. Zamiast pięciu plików masz biome.json. Formatter, linter, organizacja importów — wszystko w jednym miejscu.
Kompatybilność z Prettier. Biome osiąga 97% kompatybilności z Prettier (mierzone na rzeczywistym kodzie open source). Różnice są, ale są udokumentowane i w większości pomijalne dla przeciętnego projektu.
Linter z 467 regułami. Biome pokrywa reguły z @typescript-eslint, eslint-plugin-react, eslint-plugin-jsx-a11y i eslint-plugin-unicorn. Nie jest to 100% pokrycie, ale dla większości projektów wystarczy.
Od wersji 2.0 Biome oferuje też system pluginów (GritQL), który pozwala pisać własne reguły lintowania — to odpowiedź na jeden z głównych argumentów za pozostaniem przy ESLint.
Co Biome formatuje: JavaScript, TypeScript, JSX, TSX, JSON, JSONC, CSS, GraphQL, HTML (eksperymentalnie).
Czego Biome jeszcze nie robi: SCSS, Markdown, pełne wsparcie dla plików Astro/Vue/Svelte z embedded languages.
Jak przeprowadzić migrację — krok po kroku
Migracja w większości projektów zajmuje od jednej do kilku godzin. Poniżej konkretne komendy.
Krok 1: Instalacja
npm install --save-dev --save-exact @biomejs/biome
Flaga --save-exact przypina konkretną wersję Biome. Oficjalna dokumentacja to zaleca, żeby wszyscy w zespole pracowali na tej samej wersji formattera — różne wersje mogą produkować różny output, co powoduje fałszywe diffe w PR.
Krok 2: Inicjalizacja konfiguracji
npx @biomejs/biome init
Tworzy biome.json z domyślną konfiguracją. Na tym etapie masz działające narzędzie — możesz już uruchomić npx biome check . i zobaczyć, co Biome wykrywa w Twoim kodzie.
Krok 3: Migracja z ESLint
npx @biomejs/biome migrate eslint --write
Biome czyta Twój plik .eslintrc.js lub eslint.config.js, obsługuje sekcje extends, overrides i .eslintignore, i przenosi to wszystko do biome.json. Do załadowania pluginów ESLint potrzebuje Node.js, więc musi być zainstalowany razem z zależnościami projektu.
Krok 4: Migracja z Prettier
npx @biomejs/biome migrate prettier --write
Czyta .prettierrc lub prettier.config.js i przenosi ustawienia (styl wcięć, rodzaj cudzysłowów, trailing commas, szerokość linii) do sekcji formattera w biome.json.
Krok 5: Aktualizacja skryptów w package.json
{
"scripts": {
"lint": "biome lint .",
"format": "biome format --write .",
"check": "biome check --write .",
"ci": "biome ci ."
}
}
Warto znać różnicę między biome check a biome ci. Polecenie biome check łączy formatowanie, lintowanie i organizację importów — uruchamiaj je na maszynach developerskich z flagą --write, żeby automatycznie naprawiało błędy. Polecenie biome ci jest przeznaczone dla pipeline'ów — nie modyfikuje plików, tylko raportuje błędy. W GitHub Actions wyświetla je jako adnotacje bezpośrednio przy konkretnych liniach kodu.
Krok 6: Usunięcie starych narzędzi
npm uninstall eslint prettier eslint-config-prettier eslint-plugin-react \
eslint-plugin-import @typescript-eslint/eslint-plugin @typescript-eslint/parser \
eslint-plugin-jsx-a11y eslint-plugin-unicorn
Następnie usuń pliki konfiguracyjne: .eslintrc.js, .eslintignore, .prettierrc, .prettierignore. Po ich usunięciu środowisko jest czystsze i nie ma ryzyka, że stara konfiguracja będzie kolidować z nową.
Krok 7: Weryfikacja
npx @biomejs/biome check .
Przejrzyj wyniki i zdecyduj, które ostrzeżenia są akcjonowalne, a które chcesz wyłączyć w konfiguracji.
Przykład biome.json dla projektu TypeScript
Minimalna konfiguracja, od której warto zacząć:
{
"$schema": "https://biomejs.dev/schemas/2.4.0/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": false,
"ignore": ["dist/**", "node_modules/**", "coverage/**"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "es5",
"semicolons": "always"
}
}
}
Klucz "vcs" z "useIgnoreFile": true sprawia, że Biome honoruje wpisy z .gitignore. Nie musisz powielać listy ignorowanych ścieżek w dwóch miejscach.
Jeśli pracujesz z NestJS lub projektem opartym o dependency injection, dodaj dostosowanie dla reguły useImportType:
{
"linter": {
"rules": {
"recommended": true,
"style": {
"useImportType": "off"
}
}
}
}
Biome domyślnie konwertuje import { TagService } from './tag.service' na import type { TagService } from './tag.service'. W normalnym kodzie TypeScript to dobra praktyka. W NestJS psuje dependency injection, bo runtime nie widzi wartości — tylko typ. Lepiej wyłączyć tę regułę globalnie niż dodawać komentarze // biome-ignore w każdym serwisie.
Znane ograniczenia i gotchas
Biome to dojrzałe narzędzie, ale nie jest idealne. Uczciwa lista rzeczy, na które trafisz:
Pierwsze uruchomienie formattera zmienia tysiące linii. Mimo 97% kompatybilności z Prettier, różnice w trailing commas, cudzysłowach czy bracket spacing mogą wygenerować ogromny diff przy pierwszym biome format --write. To nie jest błąd — to jednorazowy koszt. Zrób to jako osobny commit z opisem chore: apply Biome formatting, żeby historia repozytorium była czytelna i żeby git blame pozostał użyteczny.
Brak reguł type-aware. Reguły wymagające informacji o typach TypeScript — jak @typescript-eslint/no-floating-promises czy @typescript-eslint/no-misused-promises — nie są jeszcze w pełni dostępne. Biome pracuje nad tym od wersji 2.0 (multi-file analysis), ale na dziś to luka. Jeśli te reguły są dla Ciebie krytyczne, możesz uruchamiać tsc --noEmit jako osobny krok w CI zamiast polegać na lintrze.
Brak wsparcia dla SCSS. Jeśli projekt używa SCSS, Biome go pominie. Stylelint pozostaje potrzebny dla SCSS — ale przynajmniej możesz go ograniczyć tylko do stylowania i nie mieszać z lintowaniem logiki.
Reguła noForEach może być irytująca. Biome zaleca zastępowanie .forEach() pętlą for...of. Dla wielu zespołów to zbyt opiniotwórcze. Wyłącz ją w konfiguracji:
"complexity": {
"noForEach": "off"
}
WebStorm plugin jest mniej stabilny niż VS Code. Na początku 2026 pojawiły się raporty o crashach pluginu JetBrains. VS Code extension (biomejs.biome) działa bez problemów.
Migracja dużych projektów wymaga stopniowego podejścia. Oficjalny przewodnik zaleca, żeby przy dużym monorepo zacząć od włączenia samego formattera, a linter uruchamiać z --diagnostic-level=error, żeby na start widzieć tylko błędy krytyczne, nie wszystkie ostrzeżenia.
Integracja z VS Code i CI/CD
VS Code
Zainstaluj rozszerzenie biomejs.biome z Visual Studio Code Marketplace. Następnie zaktualizuj settings.json workspace:
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescript]": { "editor.defaultFormatter": "biomejs.biome" },
"[json]": { "editor.defaultFormatter": "biomejs.biome" }
}
Po migracji wyłącz (lub odinstaluj) rozszerzenia ESLint i Prettier dla tego workspace. Jeśli zostawisz je aktywne, mogą produkować konfliktujące sugestie.
GitHub Actions
Biome ma oficjalny action, który automatycznie wykrywa wersję z package.json lub z pola $schema w biome.json:
name: Code quality
on:
push:
pull_request:
jobs:
quality:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Biome
uses: biomejs/setup-biome@v2
with:
version: latest
- name: Run Biome
run: biome ci .
Komenda biome ci w GitHub Actions wyświetla błędy jako adnotacje bezpośrednio przy konkretnych liniach kodu w interfejsie PR. Nie musisz przeglądać logów — błędy widać od razu przy przeglądaniu zmian.
Jeśli Twój biome.json używa extends z zewnętrznego pakietu, dodaj przed krokiem Setup Biome instalację zależności (npm ci), żeby Biome mógł załadować pakiet.
Widok GitHub Actions z adnotacjami Biome bezpośrednio przy liniach kodu w diff PR
Co możesz wdrożyć już dziś
Migracja nie musi być zdarzeniem "all-or-nothing". Możesz zacząć małymi krokami:
Dzisiaj — 15 minut:
Zainstaluj Biome w jednym projekcie lub pakiecie, uruchom npx biome check . i przejrzyj wyniki. Nie musisz niczego zmieniać — po prostu sprawdź, co Biome wykrywa w porównaniu do aktualnego ESLint.
W tym tygodniu — 1–2 godziny:
Przeprowadź pełną migrację w projekcie, który ma prostą konfigurację ESLint (bez niestandardowych pluginów firmowych). Użyj komend biome migrate eslint i biome migrate prettier. Zrób commit z formatowaniem jako osobną zmianę.
W tym miesiącu:
Skonfiguruj biome ci w GitHub Actions lub GitLab CI. Zaaktualizuj VS Code settings dla całego zespołu (wrzuć .vscode/settings.json do repozytorium). Usuń stare pakiety z package.json.
Dla monorepo:
Zastąp husky + lint-staged jedną komendą biome check --staged . jako pre-commit hook. Możesz też użyć lefthook zamiast husky — jest szybszy i Biome świetnie z nim współpracuje.
Jeśli projekt używa NestJS, Angular lub innego frameworku z dependency injection, pamiętaj o wyłączeniu reguły useImportType przed pierwszym uruchomieniem biome check --write.
Ile możesz na tym zyskać
Liczby z produkcyjnych migracji, nie marketingowych prezentacji:
Czas lintowania: Monorepo z ~200,000 linii kodu — z 7 minut do 1.6 sekundy. To ~250-krotne przyspieszenie. Na mniejszych projektach skala jest mniejsza: projekt z 45 plikami Next.js schodzi z 3.2 sekundy do 0.4 sekundy (8x szybciej), projekt z 180 plikami — z 12.8 do 1.1 sekundy (11.6x szybciej).
Czas CI miesięcznie: Jeden z projektów po migracji zaoszczędził ponad 1,000 minut CI miesięcznie. Przy typowych cenach runners w GitHub Actions (ok. $0.008/minutę) to kilkaset złotych oszczędności — ale ważniejsze jest to, że PR przechodzą szybciej i developerzy nie czekają na feedback.
Pre-commit hooki: Z 8 sekund do praktycznie natychmiastowych. Hooki, które działają błyskawicznie, są faktycznie używane — i faktycznie łapią błędy przed commitem.
Liczba pakietów: Typowa migracja usuwa 8–12 pakietów npm z package.json. Mniej zależności to mniejsze node_modules, szybszy npm install i mniej podatności do śledzenia.
Onboarding: Nowy developer widzi jeden plik biome.json zamiast czterech różnych plików konfiguracyjnych. Nie musi rozumieć, dlaczego eslint-config-prettier wyłącza reguły ESLint, żeby nie kolidowały z Prettier — bo tego problemu po prostu nie ma.
Biome nie jest odpowiedzią na każdy problem z jakością kodu. Jeśli intensywnie korzystasz z type-aware rules z @typescript-eslint, SCSS, lub masz firmowe pluginy ESLint bez odpowiednika — migracja wymaga dodatkowego planowania. Ale dla większości projektów TypeScript/JavaScript, które używają standardowego zestawu reguł, Biome jest gotowy do produkcji i przynosi realne oszczędności od pierwszego dnia.