Oto prawda, którą w pełni doceniłem dopiero po latach: to, jak wygląda Twój kod, ma prawie takie samo znaczenie jak to, co robi. Dobrze sformatowany kod jest szybciej przeglądany, ma mniej błędów i jest dramatycznie łatwiejszy w utrzymaniu. Z drugiej strony, prawidłowo zminifikowany kod ładuje się szybciej i oszczędza przepustowość Twoich użytkowników.

Porozmawiajmy o obu stronach tego medalu.

Dlaczego formatowanie jest ważniejsze niż myślisz

Widziałem zespoły marnujące godziny w przeglądach kodu, debatując o tabulatorach vs spacjach, średnikach czy umiejscowieniu nawiasów klamrowych. To czas, który nie jest poświęcany na dostarczanie funkcjonalności. Konsekwentne formatowanie całkowicie eliminuje te debaty.

Ale to nie tylko kwestia harmonii zespołu. Jednolicie sformatowany kod sprawia, że błędy są łatwiejsze do wykrycia. Rozważ to:

javascript

To wywołanie sendNotification() wykonuje się za każdym razem, nie tylko dla adminów — wcięcie jest mylące. Z obowiązkowymi nawiasami klamrowymi (wymuszanymi przez większość formatterów) ten błąd jest oczywisty:

javascript

Najlepsze praktyki formatowania

Wybierz styl i zautomatyzuj go. Nie polegaj na ludziach w kwestii konsekwentnego formatowania. Użyj Prettiera — jest stanowczy w swoich opiniach i to jest właśnie zaletą. Skonfiguruj go, by uruchamiał się przy zapisie, i nigdy więcej nie myśl o formatowaniu.

Wcięcia: 2 spacje to konwencja JavaScript/TypeScript. Przewodnik stylu Google, przewodnik Airbnb i Standard Style — wszystkie się co do tego zgadzają.

Średniki: Używaj ich. Tak, JavaScript ma ASI (Automatic Semicolon Insertion), ale ma też dobrze udokumentowane pułapki. Po prostu dodawaj średniki.

Długość linii: Trzymaj linie poniżej 80-100 znaków. Długie linie wymuszają poziome przewijanie i utrudniają czytanie diffów.

Jak naprawdę działa minifikacja

Minifikacja to przeciwieństwo formatowania — sprawia, że kod jest jak najmniejszy dla produkcji. Oto co robi minifikator jak Terser:

  • Usuwa białe znaki i komentarze — Wszystkie te spacje, tabulatory, nowe linie i komentarze // TODO: fix later? Znikają.
  • Skraca nazwy zmiennychuserAccountBalance staje się a. calculateMonthlyPayment staje się b. Tylko zmienne lokalne — nie zmieni nazw rzeczy, do których może odwoływać się zewnętrzny kod.
  • Eliminuje martwy kod — Jeśli funkcja nigdy nie jest wywoływana, zostaje usunięta.
  • Wstępnie oblicza stałeconst TAX_RATE = 0.2; total * (1 + TAX_RATE) staje się total*1.2.

Rezultat? Typowo 50-70% redukcji rozmiaru pliku. Oto porównanie przed i po:

Przed (czytelny, 147 bajtów):

javascript

Po (zminifikowany, 62 bajty):

javascript

Ta sama funkcjonalność, 58% mniejszy.

Nie zapomnij o Source Maps

Zminifikowany kod jest nieczytelny, co sprawia, że debugowanie błędów na produkcji jest bolesne. Source maps rozwiązują to, mapując zminifikowany kod z powrotem do oryginalnego źródła. Każde nowoczesne narzędzie do budowania je generuje — upewnij się, że przesyłasz je do swojego serwisu śledzenia błędów (Sentry, Bugsnag, itp.).

Praktyczny workflow

Praktyczny workflow

W developmencie: pisz sformatowany, czytelny kod. Używaj Prettier + ESLint do automatycznego formatowania i lintowania. Na produkcji: minifikuj wszystko przez swoje narzędzie do budowania (webpack, Vite, esbuild).

Konfiguracja Prettier + ESLint w 60 sekund

Jeśli jeszcze nie zautomatyzowałeś formatowania, oto najszybsza konfiguracja:

javascript

Następnie dodaj skrypt formatowania do swojego package.json: "format": "prettier --write src/**/*.{js,ts,jsx,tsx}". Uruchom go raz, aby sformatować całą bazę kodu, i ustaw edytor na formatowanie przy zapisie. Początkowy diff może być ogromny, ale potem debaty o formatowaniu skończą się na zawsze.

Tree Shaking vs minifikacja

Ludzie często mylą te dwie techniki, ale to różne optymalizacje, które współpracują ze sobą:

  • Minifikacja zmniejsza poszczególne pliki, usuwając białe znaki, skracając nazwy i wstępnie obliczając wyrażenia. Nie usuwa nieużywanych eksportów.
  • Tree shaking eliminuje nieużywany kod między modułami. Jeśli importujesz tylko { debounce } z lodash-es, tree shaking usuwa z bundla wszystko inne.

Obie techniki są niezbędne dla buildów produkcyjnych. Nowoczesne bundlery jak Vite i esbuild robią to automatycznie — upewnij się tylko, że używasz importów ES modules (import) zamiast CommonJS (require), aby tree shaking mógł przeanalizować Twój graf zależności.

Typowe pułapki minifikacji

1. Poleganie na function.name na produkcji. Minifikatory zmieniają nazwy funkcji, więc myFunction.name zwróci "a" lub coś równie bezużytecznego na produkcji. Jeśli potrzebujesz stabilnych nazw funkcji (do logowania, śledzenia błędów lub refleksji), używaj zamiast tego jawnych identyfikatorów tekstowych.

2. Minifikacja kodu używającego eval(). eval() może odwoływać się do dowolnej zmiennej po nazwie, więc minifikator nie może bezpiecznie zmieniać nazw w tym zakresie. Większość minifikatorów pomija zmianę nazw w funkcjach zawierających eval(), ale to znacząco ogranicza optymalizację. Unikaj eval() całkowicie, jeśli to możliwe.

3. Brak testowania zminifikowanych buildów. Niektóre błędy pojawiają się dopiero po minifikacji — zwłaszcza przy manglowaniu nazw właściwości. Zawsze uruchamiaj swoje testy na buildzie produkcyjnym, nie tylko deweloperskim.

Mierzenie rozmiaru bundla

Minifikacja ma znaczenie tylko wtedy, gdy wiesz, od czego zaczynasz. Oto szybkie sposoby sprawdzenia rozmiaru bundla:

javascript

Te narzędzia generują wizualne mapy drzewaste pokazujące dokładnie, które zależności pochłaniają Twój bundle. Możesz być zaskoczony — kiedyś znalazłem projekt, w którym locale moment.js stanowiły 500KB finalnego bundla. Przejście na dayjs zaoszczędziło 490KB natychmiast.

Prettier vs ESLint: To nie to samo

Dobra, muszę to z siebie wyrzucić, bo widzę to zamieszanie *wszędzie*: Prettier i ESLint to nie to samo narzędzie. Nie robią nawet tego samego. A mimo to ciągle widzę programistów traktujących je wymiennie. Pozwól, że to wyjaśnię.

Prettier to formater kodu. Dba o to, jak Twój kod *wygląda* — białe znaki, łamanie linii, średniki, końcowe przecinki, styl cudzysłowów, wcięcia. To wszystko. Nie wie i nie obchodzi go, czy Twój kod faktycznie działa. Mógłbyś mieć funkcję, która kasuje całą bazę danych, a Prettier po prostu upewniłby się, że jest ładnie wcięta.

ESLint z kolei to linter. Dba o *jakość* kodu — nieużywane zmienne, nieosiągalny kod, potencjalne bugi, brakującą obsługę błędów, problemy z dostępnością. Łapie rzeczy, które ugryzą Cię o 2 w nocy w sobotę, gdy dostaniesz alarm.

Ale jest pewien haczyk — ESLint *też* ma wbudowane pewne reguły formatowania i tu zaczyna się bałagan. Jeśli uruchomisz oba narzędzia bez odpowiedniej konfiguracji, będą ze sobą walczyć. Prettier formatuje Twój kod w jeden sposób, ESLint narzeka, że powinno być inaczej, poprawiasz dla ESLinta, Prettier ponownie formatuje... to nieskończona pętla frustracji.

Rozwiązanie jest banalne: użyj eslint-config-prettier. Wyłącza on wszystkie reguły ESLinta, które kolidują z Prettierem, dzięki czemu ESLint skupia się na jakości kodu, a Prettier zajmuje się formatowaniem. Koniec wojen.

javascript

Zauważ, że "prettier" jest ostatnim elementem w tablicy extends? To kluczowe — musi nadpisać reguły formatowania z pluginów wymienionych wyżej. Widziałem zespoły spędzające godziny na debugowaniu konfliktów ESLint/Prettier, żeby odkryć, że mieli złą kolejność. Zaufaj mi w tej kwestii.

Moja zalecana konfiguracja: pozwól Prettierowi zajmować się wszystkim kosmetycznym, a reguły ESLinta skonfiguruj tak, by łapały prawdziwe bugi. Nie marnuj ESLinta na narzekanie na średniki, skoro Prettier już się tym zajmuje.

Wielka debata tabulatory vs spacje (rozstrzygnięta)

No dobrze, porozmawiajmy o świętej wojnie programowania. Tabulatory czy spacje? Widziałem, jak przyjaźnie kończyły się z powodu tej debaty. Widziałem wątki na Slacku ciągnące się *dniami*. Osobiście byłem świadkiem, jak starszy programista napisał 2000-słowną stronę na Confluence broniącą tabulatorów. To było wspaniałe i kompletnie niepotrzebne.

Spójrzmy na dane. Ankieta programistów Stack Overflow konsekwentnie pokazuje, że spacje są bardziej popularne — około 60-65% programistów preferuje spacje. Własna analiza GitHuba publicznych repozytoriów opowiada podobną historię.

Ale to co ciekawe: różni się to znacząco w zależności od języka. Go używa tabulatorów — to nawet nie podlega dyskusji, gofmt wymusza tabulatory i nikt nie kłóci się z gofmt. Python używa 4 spacji — PEP 8 tak mówi i z PEP 8 też się nie dyskutuje. JavaScript i TypeScript? Społeczność w dużej mierze przyjęła 2 spacje i praktycznie każdy poważny przewodnik stylu się z tym zgadza.

Argument o dostępności za tabulatorami jest faktycznie przekonujący — tabulatory pozwalają każdemu programiście ustawić preferowaną szerokość wizualną, co ma znaczenie dla programistów z wadami wzroku. To prawdziwy, uzasadniony powód, by preferować tabulatory.

Ale wiesz co? Oto *prawdziwie* poprawna odpowiedź i mówię to szczerze: używaj tego, co wymusza Twój formater, i przestań się kłócić. Jeśli Twój projekt używa Prettiera z tabWidth: 2 i useTabs: false, to używasz 2 spacji. Kropka. Formater podejmuje decyzję, Ty ją akceptujesz i poświęcasz energię na rzeczy, które naprawdę mają znaczenie — na przykład, czy Twoja aplikacja się wysypie, gdy ktoś wpisze emoji w pole wyszukiwania.

Życie jest za krótkie na kłótnie o formatowanie. Niech roboty decydują.

Nowoczesna minifikacja: esbuild, SWC i rewolucja szybkości

Przez lata Terser był królem minifikacji JavaScript. Zastąpił UglifyJS, był sprawdzony w boju, działał świetnie. Jedyny problem? Jest wolny. Naprawdę *bardzo* wolny przy dużych bazach kodu. I nie mam na myśli wolności typu „idź po kawę" — mam na myśli „przemyśl swoje wybory kariery, patrząc na pipeline CI".

Wtedy pojawił się esbuild i zmienił wszystko. Napisany w Go, esbuild jest 10-100x szybszy niż Terser. Nie przesadzam — benchmarki są niemal komiczne. Projekt, który Terserowi zajmuje 30 sekund na minifikację? esbuild robi to w 300 milisekund. Kiedy zobaczyłem to po raz pierwszy, naprawdę myślałem, że coś jest zepsute, bo skończyło się tak szybko.

SWC to kolejny konkurent, napisany w Rust. Nie jest tak szybki jak esbuild w czystej minifikacji, ale to bardziej kompletny toolchain — obsługuje transpilację, bundlowanie i minifikację w jednym. Jeśli używasz Next.js, już korzystasz z SWC pod maską.

Oto przybliżone porównanie na średnim projekcie (~500 plików JS):

ToolLanguageMinification TimeNotes
TerserJavaScript~25sBattle-tested, most compatible
esbuildGo~0.3sBlazing fast, some edge cases
SWCRust~0.8sFull toolchain, great ecosystem

Czy różnica w szybkości naprawdę ma znaczenie? Szczerze, dla małego projektu z 5-sekundowym buildem, prawdopodobnie nie. Ale gdy uruchamiasz CI/CD na dużym monorepo i pipeline buildowy działa 50 razy dziennie? Te minuty szybko się sumują. Widziałem zespoły, które skróciły czas CI o 10+ minut, po prostu przechodząc z Tersera na esbuild.

Dobra wiadomość jest taka, że jeśli używasz Vite, już masz esbuild dla buildów deweloperskich i Rollup (z Terserem lub esbuildem) dla produkcji. Next.js używa SWC. Angular również eksperymentuje z esbuildem. Ekosystem szybko się tu rozwija.

Jedno słowo ostrzeżenia: esbuild i SWC nie zawsze produkują bajt-po-bajcie identyczne wyjście jak Terser. W rzadkich przypadkach bardziej agresywne optymalizacje Tersera dają nieco mniejsze bundlе. Ale mówimy o może 1-2% różnicy — w większości przypadków jest to całkowicie warte 100-krotnego przyspieszenia.

Minifikacja CSS i HTML też

Rozmawialiśmy o JavaScript, ale nie lekceważ minifikacji CSS i HTML. Poważnie, widziałem projekty, gdzie bundle CSS był *większy* niż JavaScript. Szczególnie jeśli używasz frameworka utility-first jak Tailwind (zanim PurgeCSS zrobi swoje).

Dla CSS, cssnano to standardowe narzędzie. Robi więcej niż tylko usuwanie białych znaków — łączy też zduplikowane reguły, konwertuje kolory na krótsze formaty (#ff0000 staje się #f00 lub nawet red), usuwa nadmiarowe właściwości i optymalizuje wyrażenia calc(). Na typowym arkuszu stylów możesz oczekiwać 30-50% oszczędności.

css

Dla HTML, html-minifier-terser usuwa białe znaki między tagami, opcjonalne tagi zamykające, minifikuje inline CSS i JS, usuwa komentarze HTML i składa atrybuty logiczne. Jest zaskakująco skuteczny — widziałem 20-30% redukcji na stronach z dużą ilością HTML.

A oto dobra wiadomość: jeśli używasz Angulara, Reacta, Vue lub praktycznie każdego nowoczesnego frameworka z etapem budowania, to już się dla Ciebie dzieje. Twoje narzędzie do budowania obsługuje minifikację CSS i HTML jako część buildu produkcyjnego. Ale wiedza o tym, co dzieje się pod maską, jest naprawdę przydatna — zwłaszcza gdy musisz zdebugować, dlaczego Twój produkcyjny HTML nie zgadza się ze źródłem, lub gdy próbujesz zoptymalizować tę ostatnią krytyczną ścieżkę renderowania.

Formatowanie kodu w pipeline'ach CI/CD

Posłuchaj, oto problem z „formatowaniem przy zapisie" — działa to tylko wtedy, gdy *każda osoba w zespole* ma to poprawnie skonfigurowane. I już mnie to kiedyś ugryzło. Znasz ten scenariusz: ktoś dołącza do zespołu, klonuje repo, zaczyna wprowadzać zmiany i wysyła PR z 400 zmianami formatowania pomieszanymi z 5 liniami faktycznego kodu. Przeglądanie tego PR to koszmar.

Rozwiązanie? Wymuszaj formatowanie w CI. Uczyń niemożliwym mergowanie niesformatowanego kodu. Oto jak:

Krok 1: Dodaj check CI. Dodaj prettier --check . do swojego pipeline'a CI. Kończy się kodem 1, jeśli jakikolwiek plik nie pasuje do formatowania Prettiera. Bez argumentów, bez debat — CI jest prawem.

javascript

Krok 2: Dodaj hooki pre-commit. Łapanie problemów z formatowaniem w CI jest świetne, ale jeszcze lepiej jest łapać je *zanim* kod zostanie scommitowany. Tu wkraczają Husky i lint-staged.

javascript
javascript
javascript

Piękno lint-staged polega na tym, że działa tylko na plikach, które faktycznie zmieniłeś, a nie na całej bazie kodu. Więc hook pre-commit zajmuje 1-2 sekundy zamiast 30. Nikt nie wyłączy hooka, który trwa 1 sekundę.

Widziałem zespoły przechodzące od „formatowanie to stałe źródło tarć w PR-ach" do „dosłownie nigdy nie myślimy o formatowaniu" w ciągu tygodnia od skonfigurowania tego. To jedna z tych rzeczy, gdzie 15-minutowa konfiguracja zwraca się tysiąckrotnie.

Studia przypadków rozmiaru bundla z prawdziwego świata

Porozmawiajmy o prawdziwych liczbach, bo abstrakcyjne rady o „trzymaniu bundli małych" nie są zbyt pomocne bez kontekstu. Widziałem dokładnie te scenariusze w projektach produkcyjnych, a różnice są naprawdę szokujące.

Przypadek 1: lodash vs lodash-es. To klasyk. Jeśli robisz import _ from 'lodash' i używasz tylko _.debounce, importujesz całą bibliotekę — 71,5 KB zminifikowane + gzipped. Przejdź na import { debounce } from 'lodash-es' i z tree shakingiem patrzysz na około 1,5 KB tylko dla tej funkcji. To 98% redukcji. Sprawdź sam na Bundlephobia.

Przypadek 2: moment.js vs dayjs. Moment.js był złotym standardem obsługi dat przez lata, ale waży 72,1 KB zminifikowane + gzipped — i to domyślnie zawiera wszystkie dane lokalizacyjne. Day.js ma prawie identyczne API, ale waży 2,9 KB. To nie literówka. 72 KB vs 3 KB za w zasadzie tę samą funkcjonalność. Sam zespół Moment.js teraz zaleca alternatywy.

Przypadek 3: Biblioteki ikon. To łapie ludzi cały czas. Jeśli robisz import { FaHome } from 'react-icons/fa', to jest OK — to obsługuje tree shaking. Ale niektóre biblioteki ikon nie są skonfigurowane pod tree shaking i kończysz importując setki ikon SVG, gdy potrzebujesz tylko 5. Widziałem, jak importy ikon dodawały 200+ KB do bundli. Zawsze sprawdzaj, czy Twoja biblioteka ikon obsługuje tree shaking, lub importuj ikony pojedynczo.

Przypadek 4: Biblioteki formatowania dat. Potrzebowałeś sformatować jedną datę w konkretnej strefie czasowej? Kiedyś widziałem projekt, który zaimportował moment-timezone (pełną wersję ze wszystkimi danymi stref czasowych) — to 97 KB zminifikowane + gzipped — tylko po to, by sformatować jedną datę. Natywne API Intl.DateTimeFormat obsługuje większość formatowania stref czasowych natywnie, bez kosztów dla bundla.

Lekcja nie brzmi „nigdy nie używaj zależności" — to byłoby głupie. Lekcja brzmi: wiedz, co importujesz i ile to kosztuje. Uruchom npx source-map-explorer build/static/js/*.js lub sprawdź Bundlephobia przed dodaniem tej nowej, błyszczącej biblioteki. Twoi użytkownicy na wolnych połączeniach mobilnych Ci podziękują.

Wypróbuj to sam

Musisz szybko posprzątać bałagan w kodzie? Wklej go do naszego JavaScript Formattera, by uzyskać natychmiastowy, czytelny wynik. Gotowy, by skurczyć go na produkcję? JavaScript Minifier zredukuje go do absolutnego minimum. A jeśli nie jesteś pewien, czy Twój kod jest w ogóle poprawny, przepuść go najpierw przez JavaScript Validator.