Luca Mele
Luca Mele

Performance

Node-Abhängigkeiten reduzieren für wartbare Anwendungen

Node-Abhängigkeiten reduzieren für wartbare Anwendungen
Zurück zu allen Beiträgen
·7 Min. Lesezeit

Führe `ls node_modules | wc -l` in deinem Projekt aus. Wenn dich die Zahl beunruhigt, bist du nicht allein. Das durchschnittliche JavaScript-Projekt zieht Hunderte von transitiven Abhängigkeiten nach sich, und jede einzelne ist ein Risiko: Sicherheitslücken, Breaking Changes, aufgegebene Wartung oder schlicht Aufblähung.

Nach Jahren als Leiter von Frontend-Teams bei Unternehmen wie AXA, Migros, Vontobel und UBS habe ich eine klare Meinung entwickelt: Jede Abhängigkeit muss ihren Platz verdienen. Wenn du es in 20 Zeilen selbst schreiben kannst, solltest du es wahrscheinlich tun.

Die versteckten Kosten von npm install

Abhängigkeiten sind nicht gratis. Jede bringt Kosten mit sich, die in deinem Bundle-Size-Analyzer nicht auftauchen:

Sicherheits-Angriffsfläche — jedes Paket ist Code, den du nicht geschrieben hast und nicht vollständig prüfen kannst. Der event-stream-Vorfall, der ua-parser-js-Hijack, die colors.js-Sabotage — das sind keine Einzelfälle, sondern die natürliche Konsequenz davon, Tausenden von Fremden die eigene Supply Chain anzuvertrauen.

Wartungsaufwand — Pakete werden aktualisiert, und diese Updates können Dinge kaputt machen. Je mehr Pakete du hast, desto mehr Zeit verbringst du mit Dependency-Updates statt mit dem Entwickeln von Features. Ich habe Teams erlebt, die ganze Sprints damit verbracht haben, Dependabot-Alerts abzuarbeiten.

Kognitive Belastung — neue Entwickler, die deinem Projekt beitreten, müssen nicht nur deinen Code verstehen, sondern auch jede Abstraktion, die deine Abhängigkeiten einführen. Die 'hilfreiche' Utility-Bibliothek mit 200 Funktionen bedeutet 200 Dinge, die jemand statt des Standardansatzes verwenden könnte.

Auswirkung auf die Build-Zeit — bei Migros betrieben wir ein PNPM-Monorepo mit Turbo für Bikeworld und Micasa. Als wir unseren Dependency-Tree überprüften und unnötige Pakete entfernten, wurde unsere CI-Pipeline deutlich schneller. Das ist Entwicklerzeit, die bei jedem einzelnen PR gespart wird.

Vor der Installation drei Fragen stellen

Ich setze in meinen Teams eine einfache Checkliste durch, bevor eine Abhängigkeit hinzugefügt wird:

1. Können wir das selbst in unter 50 Zeilen schreiben? Wenn ja, schreib es. Ein kleines Utility, das dir gehört, ist besser als ein Paket, das du nicht kontrollierst. Du verstehst es vollständig, es hat null transitive Abhängigkeiten und tut genau das, was du brauchst.

2. Wird dieses Paket aktiv gewartet? Prüfe das Datum des letzten Commits, offene Issues und den Bus-Faktor. Ein Paket mit einem Maintainer, der vor 8 Monaten zuletzt committed hat, ist eine tickende Zeitbombe.

3. Wie hoch ist die Anzahl der transitiven Abhängigkeiten? Installiere es isoliert und prüfe, was es mitbringt. Ich habe 'einfache' Pakete gesehen, die über 40 transitive Abhängigkeiten mitschleppen. Das sind 40 Pakete, die kaputtgehen, gekapert werden oder aufgegeben werden können.

# Check what a package actually pulls in
mkdir /tmp/dep-check && cd /tmp/dep-check
npm init -y && npm install <package-name>
ls node_modules | wc -l
# If this number surprises you, reconsider

Gängige Pakete, die du nicht brauchst

Hier sind echte Beispiele aus Projekten, die ich übernommen und aufgeräumt habe:

lodash — Modernes JavaScript hat Array.flat(), Array.flatMap(), Object.entries(), Object.fromEntries(), structuredClone() und den Optional-Chaining-Operator. Importiere die spezifische lodash-Funktion, wenn du sie wirklich brauchst (lodash/debounce), aber niemals die gesamte Bibliothek.

moment.js — Verwende die native Intl.DateTimeFormat-API oder allenfalls date-fns mit Tree-Shaking. Moment fügt deinem Bundle 300 KB hinzu und befindet sich offiziell im Wartungsmodus.

axios — Die Fetch-API ist jetzt überall verfügbar, einschliesslich Node.js 18+. Du brauchst keinen Wrapper um XMLHttpRequest mehr.

uuid — Wenn du nur eindeutige IDs brauchst, ist crypto.randomUUID() in jeder modernen Laufzeitumgebung eingebaut.

classnames / clsx — Wenn du Tailwind verwendest, hast du wahrscheinlich bereits ein cn()-Utility oder kannst eines in 3 Zeilen mit Template Literals schreiben.

// Instead of: import { v4 as uuid } from 'uuid'
const id = crypto.randomUUID();

// Instead of: import axios from 'axios'
const res = await fetch('/api/data');
const data = await res.json();

// Instead of: import _ from 'lodash'
const unique = [...new Set(items)];
const grouped = Object.groupBy(items, (i) => i.type);

Wann Abhängigkeiten sich lohnen

Ich sage nicht, dass du alle Pakete meiden sollst. Manche Probleme brauchen genuinely externe Lösungen:

Kryptografie — schreib niemals deine eigene. Verwende etablierte, geprüfte Bibliotheken.

Komplexes Parsing — Datumsanalyse mit Zeitzonen-Berücksichtigung, CSV-/XML-Parsing, Markdown-Rendering. Das sind Bereiche, in denen Grenzfälle dich auffressen.

Framework-Integrationen — wenn du Next.js, React oder Vue verwendest, existieren deren Ökosystem-Pakete aus guten Gründen. next/image optimiert Bilder besser als alles, was du selbst schreiben würdest.

Bei Vontobel hielten wir unsere Anzahl an Abhängigkeiten schlank, während wir eine komplexe Finanzplattform mit Echtzeit-WebSocket-Kursen bauten. Die Kernabhängigkeiten — React, Next.js und ein paar spezialisierte Bibliotheken zur Finanzformatierung — haben alle ihren Platz verdient. Alles andere haben wir selbst geschrieben oder Plattform-APIs verwendet.

Ein gesünderer Ansatz

Meine Faustregel: Behandle Abhängigkeiten wie Einstellungen. Jede sollte einen Prüfungsprozess durchlaufen. Löst sie ein wirklich schwieriges Problem (Kryptografie, PDF-Generierung, Bildverarbeitung)? Behalte sie. Spart sie dir 15 Minuten beim Schreiben einer Utility-Funktion? Schreib die Funktion.

Führe vierteljährlich ein Dependency-Audit durch. Entferne ungenutzte Pakete. Ersetze schwere Pakete durch leichtere Alternativen oder native APIs. Mach es zur Teamgewohnheit, nicht zu einer einmaligen Aufräumaktion.

Das Ergebnis ist ein Projekt, das schneller zu installieren, schneller zu bauen, einfacher zu prüfen und — am wichtigsten — langfristig einfacher für dein Team zu warten ist. Dein zukünftiges Ich wird dir danken, wenn du nicht einen Produktionsfehler debuggst, der durch eine transitive Abhängigkeit fünf Ebenen tief verursacht wurde, von deren Existenz du nicht einmal wusstest.