2.2 npm: Versionen und fremder Code
Nach dieser Einheit kannst du:
- Semantic Versioning erklären und Versionsbereiche mit
^und~lesen. - die Aufgabe der Sperrdatei erklären und eine Projektumgebung mit
npm installbeziehungsweisenpm cireproduzierbar wiederherstellen. - vor der Aufnahme einer Abhängigkeit beurteilen, ob sie nötig, gepflegt und vertrauenswürdig ist.
Bei dir läuft das buffet-app einwandfrei. Deine Sitznachbarin klont dasselbe Repository, tippt npm install, und womöglich verhält sich das Programm bei ihr anders. Dieselbe package.json, derselbe Befehl, trotzdem ein anderes Ergebnis. Die Ursache steckt in einem einzigen Zeichen: dem ^ vor der Versionsnummer.
flowchart LR
accTitle: Dieselbe Einkaufsliste, zwei verschiedene Lieferungen
list["package.json:<br/>zod ^4.4.3"] --> mine["dein Rechner, damals:<br/>npm install → 4.4.3"]
list --> hers["anderer Rechner, Wochen später:<br/>npm install → 4.6.5"]
mine --> same{"dasselbe<br/>Programm?"}
hers --> same
Abb. 2.6: Ohne Lieferschein liefert dieselbe Einkaufsliste an verschiedenen Tagen verschiedene Versionen (eigene Darstellung).
In Einheit 2.1 ging es um die Einkaufsliste, die package.json. Diese Einheit kümmert sich um den Lieferschein: welche Version genau geliefert wird, wie du ein Projekt auf jedem Rechner gleich herstellst und wie du beurteilst, ob ein Paket überhaupt ins Projekt darf.
Begriffe vorab
Abschnitt betitelt „Begriffe vorab“- Semantic Versioning (SemVer): eine Versionsnummer aus drei Teilen,
major.minor.patch, mit fester Bedeutung für jeden Teil. - Versionsbereich: ein Eintrag wie
^4.4.3in derpackage.json, der nicht eine, sondern mehrere Versionen erlaubt. - Sperrdatei (lock file): hält fest, welche Version jedes Pakets tatsächlich installiert wurde (
package-lock.jsonbei npm,pnpm-lock.yamlbei pnpm). - Lieferkette (supply chain): alle fremden Pakete, von denen dein Projekt direkt oder über andere Pakete abhängt. Wer ein Glied angreift, trifft alle, die es verwenden.
Im Möbelhaus-Bild aus Einheit 2.1: Die package.json ist die Einkaufsliste, die Sperrdatei der Lieferschein mit der genauen Artikelnummer jedes gelieferten Teils.
Versionen: das Kernkonzept
Abschnitt betitelt „Versionen: das Kernkonzept“An dieser Stelle entscheidet sich, ob ein Projekt in zwei Jahren noch baut. Pakete verwenden Semantic Versioning (SemVer), eine dreiteilige Nummer mit fester Bedeutung:
4 . 4 . 3 major minor patch| Teil | Wird erhöht bei | Für dich bedeutet das |
|---|---|---|
patch | Fehlerbehebung, keine Schnittstellenänderung | gefahrlos aktualisieren |
minor | neue Funktionen, bestehende bleiben gültig | gefahrlos aktualisieren |
major | Änderung, die bestehenden Code brechen kann | erst lesen, dann aktualisieren |
Diese Bedeutung ist ein Versprechen der Paketautoren, keine technische Garantie. Es wird meistens gehalten, aber nicht immer. Ein major-Sprung ist deshalb immer eine bewusste Entscheidung, nie ein nebenbei ausgeführtes Kommando.
Wo begegnet dir das? Bei Node.js selbst: node --version aus Kapitel 1 zeigt etwa v24.11.1. Die 24 ist die major-Version, und genau an ihr hängt, ob eine Fassung LTS ist oder nicht. Auch viele Handy-Apps zeigen in den Einstellungen eine dreiteilige Versionsnummer, nur hält sich nicht jede App an die SemVer-Bedeutung.
In der package.json steht deshalb selten eine exakte Version, sondern ein Bereich:
| Eintrag | Bedeutung |
|---|---|
"^4.4.3" | alles ab 4.4.3 bis unter 5.0.0 (Standard bei npm install) |
"~4.4.3" | alles ab 4.4.3 bis unter 4.5.0 (nur Patches) |
"4.4.3" | exakt diese Version (so trägt --save-exact ein, siehe Biome oben) |
Konkret heißt das für "zod": "^4.4.3": Im Oktober 2026 ist 4.6.5 die neueste 4er-Version, also installiert npm install auf einem frischen Rechner 4.6.5. Erscheint morgen 5.0.0, bleibt es bei der neuesten 4er-Version, denn der Bereich endet unter 5.0.0. Ausnahme: Bei Versionen unter 1.0.0 ist schon der minor-Teil ein Bruch-Signal, deshalb bedeutet ^0.4.2 nur “ab 0.4.2 bis unter 0.5.0”.
Die Sperrdatei
Abschnitt betitelt „Die Sperrdatei“Damit ergibt sich das Problem, das jeden Anfänger einmal erwischt: "^4.4.3" bedeutet, dass zwei Installationen an verschiedenen Tagen verschiedene Versionen ergeben können. Genau dafür gibt es die package-lock.json (pnpm: pnpm-lock.yaml). Sie hält fest, welche Version jedes einzelnen Pakets tatsächlich installiert wurde, auch jeder transitiven Abhängigkeit.
Im Möbelhaus-Bild: Die Einkaufsliste sagt “ein Kasten dieses Modells”, die Sperrdatei ist der Lieferschein mit der genauen Artikelnummer jedes gelieferten Teils. Wer nach diesem Lieferschein nachbestellt, bekommt exakt dasselbe. Die Grenze des Bildes: Ein Paket nutzt sich nicht ab und lässt sich beliebig oft identisch nachladen. Deshalb kommt nicht die Lieferung ins Repository, sondern nur der Lieferschein.
Für automatisierte Umgebungen (die CI-Pipeline aus Kapitel 13) gibt es den strengeren Befehl:
npm ci # installs exactly what the lock file says; fails if it is missing or inconsistent
pnpm install --frozen-lockfile # pnpm: same guarantee, fails if the lock file is out of datenpm ci löscht node_modules und baut die Umgebung ausschließlich aus der Sperrdatei auf. Er ist damit reproduzierbar und schneller, und er ist der Grund, warum die Sperrdatei ins Repository gehört. pnpm hat dafür keinen eigenen Befehl, sondern die Option --frozen-lockfile; sie ist in CI-Umgebungen ohnehin der Standard.
Aktualisieren
Abschnitt betitelt „Aktualisieren“npm outdated # what is out of date?npm update # update within the allowed range (no major jumps)npm install zod@5 # deliberate major jump
pnpm outdatedpnpm update # update within the allowed range (no major jumps)pnpm add zod@5 # deliberate major jumpnpm update (pnpm: pnpm update) bleibt innerhalb der Bereiche aus der package.json und ist deshalb der ungefährliche Befehl. Ein major-Sprung wird einzeln gemacht, mit einem Blick in die Änderungsliste des Pakets und einem Testlauf danach. Nie für mehrere Pakete gleichzeitig, weil sonst unklar bleibt, welcher Sprung etwas gebrochen hat.
Auflösung
Bei ^4.4.3 sind 4.4.9 und 4.6.0 erlaubt: alles ab 4.4.3 bis unter 5.0.0. Bei ~4.4.3 bleibt nur 4.4.9, weil der Bereich unter 4.5.0 endet. 4.4.2 ist in beiden Fällen zu alt, 5.0.0 in beiden Fällen ein major-Sprung. Nachprüfen kannst du das im npm SemVer Calculator.
npx: einmal ausführen statt installieren
Abschnitt betitelt „npx: einmal ausführen statt installieren“Manche Werkzeuge braucht man genau einmal, etwa den Projektgenerator eines Frameworks. npx lädt ein Paket, führt es aus und installiert es nicht dauerhaft:
npx create-next-app@latest my-project # used again in chapter 8pnpm dlx create-next-app@latest my-projectFür Werkzeuge, die im Projekt stehen, ruft npx die lokale Version auf: npx tsc nimmt den TypeScript-Compiler aus node_modules, nicht irgendeinen global installierten. Bei pnpm heißt das Gegenstück pnpm exec tsc, und pnpm dlx ist das Gegenstück zum einmaligen Ausführen.
Das ist der Grund, warum Projektwerkzeuge lokal und nicht global installiert werden: Zwei Projekte können unterschiedliche Compilerversionen brauchen, und global geht immer nur eine.
Wo begegnet dir das? In Kapitel 7 legst du mit npm create vite@latest ein React-Projekt an. Dahinter steckt derselbe Mechanismus: npm create vite führt das Paket create-vite einmal aus, ohne es dauerhaft zu installieren.
Fremden Code beurteilen
Abschnitt betitelt „Fremden Code beurteilen“Ein Paket zu installieren ist eine Entscheidung mit Folgen: Du übernimmst Code, den du nicht geschrieben hast, in dein Produkt, samt dessen Abhängigkeiten und dessen Sicherheitslücken.
Prüffragen vor jedem npm install:
- Brauche ich das wirklich? Für drei Zeilen Datumsformatierung braucht es kein Paket. Jede Abhängigkeit ist dauerhafte Wartungslast.
- Wird es gepflegt? Wann kam die letzte Veröffentlichung? Wie viele offene Fehlermeldungen liegen unbeantwortet?
- Wie verbreitet ist es? Wöchentliche Downloadzahlen sind ein grober, aber brauchbarer Hinweis: Bei vielen Benutzern fallen Probleme meist auf, bevor sie dich treffen.
- Was schleppt es mit? Ein Paket mit fünfzig eigenen Abhängigkeiten ist eine andere Entscheidung als eines ohne.
- Passt die Lizenz? Bei schulischen Projekten selten kritisch, im Berufsleben regelmäßig.
npm audit # known vulnerabilities in the dependency treenpm audit fix # update what can be updated safelynpm ls zod # who actually pulls in this package?
pnpm auditpnpm audit --fix # pnpm: adds "overrides" to pnpm-workspace.yaml instead of updating packagespnpm why zod # who actually pulls in this package?npm audit fix und pnpm audit --fix gehen unterschiedlich vor: npm aktualisiert die betroffenen Pakete, pnpm erzwingt stattdessen eine sichere Version per Override. Was das genau heißt, steht im optionalen Teil Vertiefung: Overrides bei pnpm audit --fix bei den Zusatzmaterialien.
Wo begegnet dir das? Schon bei jedem npm install: Am Ende steht eine Zeile wie found 0 vulnerabilities. Das ist eine automatische Sicherheitsprüfung. Steht dort eine Zahl größer null, zeigt npm audit die Details.
Auflösung
Schon die erste Frage: tableNumber % 2 === 0 ist eine einzige Zeile, dafür braucht es kein Paket. Jede Abhängigkeit bleibt dauerhafte Wartungslast, und sie kann eigene Abhängigkeiten mitbringen (Frage 4). Wie wenig Code genügt, um viele Projekte lahmzulegen, hat left-pad gezeigt: Elf Zeilen, die jeder selbst hätte schreiben können.
Die Alternativen
Abschnitt betitelt „Die Alternativen“npm ist nicht das einzige Werkzeug seiner Art. Du wirst in fremden Projekten auf yarn, pnpm und bun stoßen. Alle lesen dieselbe package.json, unterscheiden sich aber in Geschwindigkeit, Plattenverbrauch und eigener Sperrdatei (yarn.lock, pnpm-lock.yaml, bun.lock).
Zwei Regeln genügen fürs Erste. In einem Projekt wird ein Paketmanager verwendet; zwei Sperrdateien nebeneinander sind ein handfester Fehler. Und welcher es ist, erkennst du an der vorhandenen Sperrdatei, nicht an der Gewohnheit. Im Unterricht zeigen wir npm, weil es mit Node.js mitkommt und keine Zusatzinstallation braucht; pnpm ist jederzeit gleichwertig verwendbar.
Wo begegnet dir das? In vielen Installationsanleitungen, etwa bei Vite oder Next.js, stehen die Befehle für npm, pnpm, yarn und bun nebeneinander. Du nimmst die Zeile, die zur Sperrdatei im Projekt passt.
Übersicht: npm und pnpm im Vergleich
Abschnitt betitelt „Übersicht: npm und pnpm im Vergleich“| Aufgabe | npm | pnpm |
|---|---|---|
| Projekt anlegen | npm init -y | pnpm init |
| Skript ausführen | npm run dev | pnpm run dev (oder pnpm dev) |
| Paket installieren | npm install zod | pnpm add zod |
| Entwicklungspaket | npm install -D vitest | pnpm add -D vitest |
| Alles wiederherstellen | npm install | pnpm install |
| Exakt nach Sperrdatei | npm ci | pnpm install --frozen-lockfile |
| Paket entfernen | npm uninstall zod | pnpm remove zod |
| Veraltetes anzeigen | npm outdated | pnpm outdated |
| Im erlaubten Bereich aktualisieren | npm update | pnpm update |
| Einmal ausführen | npx <paket> | pnpm dlx <paket> |
| Lokales Werkzeug aufrufen | npx tsc | pnpm exec tsc |
| Sicherheitsprüfung | npm audit | pnpm audit |
| Wer holt dieses Paket? | npm ls zod | pnpm why zod |
| Sperrdatei | package-lock.json | pnpm-lock.yaml |
pnpm ist nicht Teil von Node.js und muss einmalig installiert werden, etwa mit npm install --global pnpm; weitere Wege beschreibt die pnpm-Installationsanleitung. Das ist eine der wenigen Ausnahmen von der Regel “nichts global”: Der Paketmanager selbst ist ein Werkzeug des Rechners, keines des Projekts. Damit im Team alle dieselbe Version verwenden, trägt pnpm init das Feld packageManager in die package.json ein.
Auflösung
Die Sperrdatei verrät pnpm, also pnpm install (in einer CI-Pipeline pnpm install --frozen-lockfile). npm install liest die pnpm-lock.yaml nicht, löst die Versionsbereiche neu auf und legt eine zusätzliche package-lock.json an. Danach liegen zwei Sperrdateien im Projekt, und im Team installiert womöglich jede:r etwas anderes.
Stolperfallen
Abschnitt betitelt „Stolperfallen“package-lock.jsonignoriert oder gelöscht: die Ursache hinter den meisten “bei mir läuft es aber”-Fällen.- Global statt lokal installiert: funktioniert auf dem eigenen Rechner und nirgends sonst; das Projekt ist ohne die Dokumentation dieser Handgriffe nicht lauffähig.
npm audit fix --forceblind ausgeführt: Das--forceerlaubtmajor-Sprünge und bricht dabei gern das Projekt. Erst lesen, dann entscheiden.- Zwei Sperrdateien nebeneinander:
package-lock.jsonundpnpm-lock.yaml(oderbun.lock) im selben Projekt, und jede:r im Team installiert etwas anderes. Eine löschen, einen Paketmanager festlegen.
Zusatzmaterialien
Abschnitt betitelt „Zusatzmaterialien“- npm-Dokumentation zu
npm ci- erklärt genau, wannnpm ciabbricht und warum das gewollt ist. - semver.org - kurz und präzise: was
major,minorundpatchversprechen. - npm SemVer Calculator - zeigt für ein beliebiges Paket, welche Versionen ein Bereich trifft; hilfreich, um
^und~wirklich zu verstehen. - npmjs.com - die Paketsuche selbst: Hier stehen Downloadzahlen, letzte Veröffentlichung und Abhängigkeiten, also alles für die Prüffragen oben.
- pnpm-Dokumentation - Befehlsreferenz und Begründung für pnpm; hilfreich, wenn du statt npm damit arbeitest.
Vertiefung: Overrides bei pnpm audit --fix
Abschnitt betitelt „Vertiefung: Overrides bei pnpm audit --fix“Dieser Teil ist optional und kommt im Unterricht nicht vor. Er ist für dich, wenn du mit pnpm arbeitest.
npm audit fix repariert eine Sicherheitslücke, indem es die betroffenen Pakete auf eine neuere Version hebt, soweit die Versionsbereiche in der package.json das erlauben. pnpm audit --fix aktualisiert dagegen kein Paket. Es trägt einen Override ein, also eine Zwangsversion: Egal welches Paket im Abhängigkeitsbaum die verwundbare Version verlangt, pnpm installiert dort die reparierte.
Ein Beispiel: Das alte Paket [email protected] zieht [email protected] mit, für das zwei bekannte Lücken gemeldet sind. pnpm audit --fix schreibt daraufhin in die pnpm-workspace.yaml:
overrides: minimist@<0.2.1: ^0.2.1 minimist@<0.2.4: ^0.2.4Erst ein anschließendes pnpm install wendet die Overrides an. Danach hängt unter [email protected] die reparierte Version [email protected], und pnpm audit meldet keine Lücke mehr. mkdirp selbst bleibt auf 0.5.1.
Vorteil und Preis liegen nah beieinander:
- Vorteil: Auch Lücken tief im Abhängigkeitsbaum lassen sich sofort schließen, selbst wenn das Paket dazwischen (hier
mkdirp) noch keine neue Version veröffentlicht hat. - Preis: Das Paket dazwischen bekommt eine Version, mit der es nie getestet wurde. Bei einem Sprung über eine Major-Version kann es dadurch brechen. Nach einem
--fixalso immer testen. - Aufräumen: Overrides bleiben stehen, bis du sie löschst, auch wenn das Paket dazwischen die Lücke längst selbst behoben hat. Schau sie dir bei jedem größeren Update an und entferne, was nicht mehr nötig ist.
- Für alle gültig: Die
pnpm-workspace.yamlwird eingecheckt. Ein Override gilt also für das ganze Team, nicht nur auf deinem Rechner.
Seit pnpm 11 gibt es auch pnpm audit --fix=update: Es hebt die verwundbaren Pakete in der Sperrdatei an, so wie npm. Im Beispiel hilft das aber nicht, denn [email protected] verlangt genau [email protected]. --fix=update meldet dann 0 vulnerabilities were fixed. Genau für solche Fälle gibt es Overrides.
In älteren Projekten findest du Overrides manchmal noch im Feld pnpm.overrides der package.json. Seit pnpm 11 liest pnpm Einstellungen aus diesem Feld nicht mehr, sie gehören in die pnpm-workspace.yaml.
- pnpm-Dokumentation:
pnpm audit- alle Optionen vonpnpm audit, auch--fixund--fix=update. - pnpm-Dokumentation:
overrides- wie Overrides aufgebaut sind und welche Schreibweisen es gibt.
Zum Anhören
Abschnitt betitelt „Zum Anhören“Du kannst dir diese Einheit kostenlos mit der Vorlesefunktion deines Browsers oder Betriebssystems vorlesen lassen (etwa „Laut vorlesen” in Edge), ohne Konto und ohne Upload. Freiwillig kannst du mit Google NotebookLM (Google-Konto nötig) aus diesem öffentlichen Text eine gesprochene Zusammenfassung erzeugen, auch auf Deutsch.
Lernergebnisse
Abschnitt betitelt „Lernergebnisse“Du kannst jetzt:
- Semantic Versioning erklären und Versionsbereiche mit
^und~lesen. - die Aufgabe der Sperrdatei erklären und eine Projektumgebung mit
npm installbeziehungsweisenpm ci(pnpm:pnpm installbeziehungsweisepnpm install --frozen-lockfile) reproduzierbar wiederherstellen. - vor der Aufnahme einer Abhängigkeit beurteilen, ob sie nötig, gepflegt und vertrauenswürdig ist.
Passende Übungen
Abschnitt betitelt „Passende Übungen“- Aufgabe 01 - TypeScript-Spickzettel, Stufe Wesentlich_überwiegend fertigstellen (Sperrdatei,
node_modules/löschen und mitnpm ciwiederherstellen)
Quellen
Abschnitt betitelt „Quellen“npm ci: Verhalten und Abgrenzung zunpm install→ npm-Dokumentation: npm cinpm createführt das Paketcreate-<name>aus → npm-Dokumentation: npm init- Semantic Versioning, Bedeutung von
^und~→ semver.org - Welche Version ein Versionsbereich wie
^4.4.3tatsächlich trifft → npm SemVer Calculator - npm-Befehle allgemein (
install,audit,outdated,update) → npm Docs - npm-Registry, Paketsuche, Downloadzahlen → npmjs.com
- pnpm-Befehle,
packageManager-Feld → pnpm-Dokumentation - pnpm-Installation → pnpm-Installationsanleitung
pnpm audit --fixträgt Overrides in diepnpm-workspace.yamlein,--fix=updateseit pnpm 11 → pnpm-Dokumentation: pnpm audit- Aufbau von
overrides→ pnpm-Dokumentation: overrides - Seit pnpm 11 keine Einstellungen mehr aus dem Feld
pnpmderpackage.json→ pnpm-Dokumentation: package.json - Beispiel
[email protected]/[email protected], Ausgaben vonpnpm audit --fixund--fix=update→ eigener Test mit pnpm 12.5.1 und Node.js 24.11.1 (Windows, Oktober 2026) - Abb. 2.6 (dieselbe Einkaufsliste, zwei Lieferungen, Mermaid-Diagramm), Abb. 2.7 (SemVer-Bereiche), Abb. 2.8 (Sperrdatei und
npm ci), Abb. 2.9 (npx), Abb. 2.10 (Lieferkette), Abb. 2.11 (Sperrdateien und Paketmanager) → eigene Darstellung - Vorfall rund um das Paket
event-stream(2018) → npm-Blog: Details about the event-stream incident - Vorfall rund um das Paket
left-pad(2016) → Wikipedia: Npm left-pad incident
Autoren
Abschnitt betitelt „Autoren“- Oliver Maurhart (MAO)
- Michael Gabriel (GAM)
CC-BY-SA