Jak wykryć nieużywane biblioteki JS

Jak wykryć nieużywane biblioteki JS

Wiele aplikacji webowych rośnie wraz z dodawaniem nowych funkcji, bibliotek i szybkich rozwiązań z internetu. Po pewnym czasie część z tych zależności przestaje być używana, ale nadal jest ładowana do przeglądarki lub trzymana w projekcie. W efekcie rośnie rozmiar bundle, pogarsza się performance i wydłużają się czasy ładowania przez dodatkowe zapytania HTTP. Ten artykuł pokazuje praktyczne metody wykrywania i weryfikowania nieużywanech bibliotek w projektach JS, narzędzia które warto znać oraz kroki, jak bezpiecznie je usunąć.

Dlaczego warto wykrywać i usuwać nieużywane biblioteki

Usuwanie zbędnych zależności to nie tylko kwestia estetyki projektu. Konsekwencje ekonomiczne i techniczne są realne:

  • Wydłużony czas ładowania strony i gorsze wyniki w narzędziach takich jak Lighthouse — negatywny wpływ na SEO i performance.
  • Większy bundle oznacza więcej pamięci i wolniejsze parsowanie JS po stronie klienta.
  • Potencjalne luki bezpieczeństwa — nieaktualizowane pakiety stają się wektorem podatności.
  • Zwiększone koszty utrzymania: trudniej robić refaktoryzacje i aktualizacje, gdy repozytorium zawiera niepotrzebne zależności.
  • Zwiększona złożoność dependency tree — trudności z debugowaniem i konfliktami wersji.

Dzięki regularnej kontroli zależności można zmniejszyć ryzyko i poprawić doświadczenie użytkownika końcowego.

Metody i narzędzia do wykrywania nieużywanych bibliotek

Istnieje kilka podejść do identyfikacji nieużywanych pakietów — od analiz statycznych, przez analizę bundla, po monitorowanie runtime. Każde ma zalety i ograniczenia. Poniżej opisane są najważniejsze techniki oraz narzędzia, które warto wprowadzić do workflow.

Analiza runtime w przeglądarce

  • Coverage w Chrome DevTools: otwórz DevTools → More tools → Coverage. Wykonaj odświeżenie i sprawdź, które pliki JS mają niski procent wykorzystania. To szybki sposób, aby zidentyfikować kod, który jest pobierany, ale nie wykonywany.
  • Lighthouse: testuje stronę pod kątem wydajności i raportuje „Unused JavaScript”. Zwróć uwagę na pliki i fragmenty kodu wskazane jako nieużywane.
  • Network + Performance: sprawdź, które pliki są ładowane przy starcie i ile czasu zajmuje ich pobranie i wykonanie.

Analiza bundla

Jeżeli używasz bundlera (webpack, Rollup, Parcel), analiza wynikowego bundle pozwala zobaczyć, które pakiety zajmują najwięcej miejsca i które moduły są ładowane.

  • webpack-bundle-analyzer — tworzy interaktywną mapę graficzną rozmiaru pakietów. Pozwoli szybko zlokalizować duże biblioteki.
  • source-map-explorer — analizuje plik JS wraz z mapą źródłową i pokazuje rozkład rozmiaru według modułów.
  • Rollup i narzędzia do statycznej analizy generują raporty, które pomagają zidentyfikować niezoptymalizowane moduły.

Analiza zależności w kodzie (statyczna)

  • depcheck — popularne narzędzie do wykrywania nieużywanych zależności w package.json. Mimo przydatności potrafi dawać fałszywe alarmy przy dynamicznych importach.
  • eslint z regułami dotyczącymi nieużywanych importów (np. no-unused-vars, import/no-extraneous-dependencies) — pomocne przy znajdywaniu nieużywanych modułów w kodzie źródłowym.
  • npm-check / npm-check-updates — narzędzia do przeglądu zależności, ich stanu i przydatności.

Analiza produkcyjna i telemetryczna

Analizy w środowisku produkcyjnym pokazują, co rzeczywiście trafia do użytkowników:

  • Logi CDN i analizy ruchu — które pliki są pobierane przez użytkowników (czy np. większość użytkowników nie pobiera pewnych chunków).
  • Monitoring wydajności (RUM — Real User Monitoring) — metryki ładowania i wykonania skryptów u rzeczywistych użytkowników.
  • Mapy source map i narzędzia do profilowania (np. Sentry, New Relic) — wyłapywanie długich zrzutów stosu, fragmentów kodu wpływających na długi czas działania.

Praktyczny proces wykrywania — krok po kroku

Poniżej znajduje się proponowany cykl, który można wdrożyć w zespole, aby systematycznie wychwytywać nieużywane biblioteki i minimalizować ryzyko błędnych usunięć.

  • Krok 1 — inwentaryzacja: wygeneruj listę zależności z package.json i zidentyfikuj te, które ostatnio zostały dodane lub niezmieniane od dłuższego czasu. Użyj npm ls / yarn list dla drzewa zależności.
  • Krok 2 — statyczna analiza: uruchom depcheck i eslint, aby zidentyfikować potencjalne nieużywane pakiety. Zanotuj ostrzeżenia i przygotuj listę do weryfikacji manualnej.
  • Krok 3 — analiza bundla: stwórz produkcyjny build i przeanalizuj wynik za pomocą source-map-explorer lub webpack-bundle-analyzer. Zwróć uwagę na duże paczki i moduły, które mogą być ładowane niepotrzebnie.
  • Krok 4 — runtime verification: użyj Coverage w Chrome oraz Lighthouse, żeby potwierdzić, które pliki są faktycznie ignorowane przy użytkowaniu. Sprawdź przypadki edge-case: dynamiczne importy, feature flags, lazy loading.
  • Krok 5 — eksperyment w oddzielnym branchu: usuń podejrzane zależności lokalnie, uruchom testy oraz build. Ręcznie przetestuj krytyczne ścieżki aplikacji i uruchom automatyczne testy e2e.
  • Krok 6 — wdrożenie kontrolowane: po pozytywnej weryfikacji, wdróż zmiany stopniowo (canary, staged rollout) i monitoruj telemetrykę (Bugsnag/Sentry, RUM).

Automatyzacja i integracja w CI/CD

Aby nie dopuścić do ponownego narastania nieużywanych zależności, warto zautomatyzować kontrole w pipeline CI. Oto sugestie jak to zrobić:

  • Uruchamiaj depcheck i eslint jako job w CI; oznacz jako ostrzeżenie lub błąd w zależności od reguł zespołu.
  • Generuj raporty webpack-bundle-analyzer w formie statycznych plików HTML i przesyłaj je jako artefakt builda — pozwala to reviewować zmiany w bundle między kolejnymi PR-ami.
  • Wprowadź threshold size checks: jeśli build zwiększy się powyżej określonego progu, pipeline powinien zgłosić błąd lub ostrzeżenie.
  • Integracja z review — dodawaj checklistę do pull requestów (np. „Nowa zależność? Dlaczego?”), wymagaj uzasadnienia i ewaluacji rozmiaru.

Jak bezpiecznie usuwać i zastępować biblioteki

Usuwanie zależności może być prostsze lub bardziej ryzykowne w zależności od sposobu jej użycia. Oto praktyczne wskazówki, aby przeprowadzić to bezboleśnie:

  • Sprawdź wszystkie importy i require — użyj wyszukiwania w repozytorium. Upewnij się, że nie ma dynamicznych odwołań typu require(someVar) lub eval, które uniemożliwią statyczne wykrycie użycia.
  • Uważaj na biblioteki z sideEffects — nawet gdy nie ma jawnych importów, pliki mogą wykonywać kod przy ładowaniu. W package.json sprawdź pole sideEffects.
  • Zastąp duże biblioteki lżejszymi modułami albo natywnymi API. Przykłady: zamiast lodash.get użyj optional chaining (?.), zamiast moment.js użyj date-fns lub Temporal / Intl.
  • W przypadku bibliotek używanych tylko w niektórych ścieżkach, rozważ lazy loading (dynamic import) i code splitting, aby nie ładować ich na start.
  • Jeśli pojawiły się fałszywe fałszywe alarmy: oznacz pliki jako używane lub dostosuj reguły depcheck/ESLint. Dokumentuj wyjątki, żeby nie gubić kontekstu.

Typowe pułapki i jak ich unikać

Poniżej lista częstych problemów, które mogą prowadzić do błędnego usunięcia zależności lub do błędnych wyników analizy:

  • Dynamiczne importy i require — narzędzia statyczne mogą nie wykryć użycia. Ręczna weryfikacja jest konieczna.
  • Side effects — biblioteki, które modyfikują global state lub prototypy, mogą być ładowane tylko dla efektu ubocznego.
  • Pliki ładowane przez zewnętrzne runtime’y — przykładowo pluginy do bundlera, build-time scripts — mogą być pominięte przez proste skrypty sprawdzające.
  • Posługuj się source maps przy analizie bundla — bez nich analiza rozmiaru będzie mniej precyzyjna.
  • Fałszywe pozytywy w depcheck — sprawdź listę ręcznie zanim usuniesz zależność.

Przykładowe narzędzia i polecenia

Poniżej krótkie wskazówki jak uruchomić najważniejsze narzędzia:

  • depcheck: npx depcheck — raport nieużywanych dependencies i devDependencies.
  • webpack-bundle-analyzer: po buildzie uruchom npx webpack-bundle-analyzer dist/stats.json lub zintegruj plugin w konfiguracji webpacka.
  • source-map-explorer: npx source-map-explorer dist/main.js dist/main.js.map.
  • Chrome Coverage: DevTools → Coverage → Start instrumenting and reload.
  • Lighthouse: DevTools → Lighthouse lub użyj npx lighthouse https://example.com.
  • npm-check: npx npm-check — interaktywny przegląd pakietów, pomocny do ręcznego odinstalowania.

Dobre praktyki przy zarządzaniu zależnościami

Wprowadzenie kilku zasad organizacyjnych zmniejszy prawdopodobieństwo, że projekt znowu zapełni się niepotrzebnymi bibliotekami:

  • Zasada „każda nowa zależność wymaga uzasadnienia” — prośba o dodanie pakietu powinna zawierać analizę rozmiaru i alternatywy.
  • Regularne przeglądy pakietów (quarterly dependency audits) — audytuje się nie tylko nieużywane, ale i podatne pakiety.
  • Preferowanie natywnych rozwiązań i lekkich mikro-bibliotek zamiast monolitycznych pakietów.
  • Utrzymanie testów end-to-end i integracyjnych, które pomagają wykryć regresje po usunięciu zależności.
  • Oznaczanie wyjątków i dokumentowanie powodów pozostawienia nienaruszonych pakietów (np. compatibility, legacy code).

Przykładowe scenariusze i rozwiązania

Kilka typowych przypadków z praktyki i rekomendowane podejścia:

  • Problem: Aplikacja ładuje moment.js globalnie, ale używa tylko formatowania dat w jednym miejscu.
    • Rozwiązanie: Zastąp moment.js lżejszym date-fns lub użyj Intl.DateTimeFormat, albo ładować moment tylko w lazy-chunk dla dedykowanej strony.
  • Problem: W package.json jest wiele devDependencies nieużywanych po refaktoryzacji.
    • Rozwiązanie: Użyj depcheck, a następnie ręcznie przetestuj i odinstaluj pakiety. Zadbaj o aktualizację skryptów w package.json.
  • Problem: Library z globalnymi side effects jest rozpoznawana jako nieużywana.
    • Rozwiązanie: Potwierdź zachowanie biblioteki, a jeśli jest potrzebna wyłącznie dla efektów ubocznych, pozostaw ją i udokumentuj powód. W przeciwnym wypadku refaktoryzuj kod tak, aby nie polegał na side effects.

Podsumowanie kroków do natychmiastowego zastosowania

Jeżeli chcesz zacząć dziś: uruchom depcheck, zrób production build i sprawdź go narzędziem typu webpack-bundle-analyzer, oraz przeprowadź Coverage w Chrome. Opracuj listę podejrzanych zależności, usuń je w oddzielnym branchu, uruchom testy i wdrożenie etapowe z RUM. Wprowadź również automatyczną kontrolę w CI, żeby wykrycia były regularne i widoczne dla zespołu.