
package-lock.json gegen package.json: Welche Datei entscheidet, was installiert wird
- VersionDude
- guides
- 8 Min. Lesezeit
Die eine erklärt, was Sie akzeptieren, in Form von Bereichen. Die andere hält fest, was tatsächlich aufgelöst wurde, bis hinunter zu Paketen, die Sie nie genannt haben. Beide zu verwechseln ist der Grund, warum „Bei mir läuft es“ 2026 noch lebt.
Zwei Dateien liegen in fast jedem JavaScript-Projekt nebeneinander, und der Abstand zwischen ihnen ist der Abstand zwischen dem, was Sie verlangt haben, und dem, was Sie bekommen haben. package.json erklärt eine Absicht: die Abhängigkeitsbereiche, die Sie akzeptieren, Ihren eigenen Namen und Ihre Version, Ihre Skripte. package-lock.json erklärt ein Ergebnis: die genaue Version jedes aufgelösten Pakets, auch derjenigen, die Sie nie genannt haben, jeweils mit der Registry-URL ihrer Herkunft und einem Integritätswert ihres Inhalts.
Die Verwechslung ist nicht akademisch. Sie ist der Mechanismus, der „Bei mir läuft es“ am Leben hält: Zwei Entwickler mit byteidentischen package.json-Dateien können in node_modules unterschiedliche Bäume vorfinden, denn ein Bereich wie ^4.17.0 akzeptiert alles unterhalb von 5.0.0, und die Antwort hängt davon ab, was die Registry an dem Tag enthielt, an dem der jeweilige Entwickler die Installation ausführte.
Was jede Datei tatsächlich erklärt

package.json trägt Bereiche, und ein Bereich ist eher eine Erlaubnis als eine Entscheidung. ^1.2.3 akzeptiert Patch- und Minor-Updates unterhalb von 2.0.0, ~1.2.3 akzeptiert nur Patches, und ein nacktes 1.2.3 akzeptiert nichts anderes. Was das Manifest nie trägt, ist Ihr transitiver Baum: Die Abhängigkeiten Ihrer Abhängigkeiten stehen nicht darin, und sie deklarieren eigene Bereiche — dort entsteht der größte Teil der Versionsdrift.
package-lock.json trägt die Auflösung. Jedes Paket im Baum erhält einen Eintrag mit der gewählten Version, der URL des heruntergeladenen Archivs und einer Integritätszeichenkette, die der Installer gegen die empfangenen Bytes prüft. Dieser Wert macht die Lockfile zu einem Lieferketten-Artefakt statt zu einer bloßen Bequemlichkeit: Ein unter derselben Versionsnummer mit anderem Inhalt neu veröffentlichtes Archiv fällt bei der Prüfung durch, statt sich stillschweigend zu installieren.
Die beiden Dateien haben auch nicht denselben Autor. Sie bearbeiten package.json. Sie bearbeiten package-lock.json nicht — npm schreibt sie, und sie von Hand zu ändern ist ein verlässlicher Weg zu einer Lockfile, die keiner Auflösung mehr entspricht, die npm tatsächlich durchführen würde.
Die Lockfile einchecken
Bei einer Anwendung: einchecken. Ohne Lockfile in der Versionsverwaltung löst Ihre CI den Baum bei jedem Lauf neu auf, und ein stromaufwärts zwischen zwei Builds veröffentlichter Patch verändert das Ausgelieferte, ohne dass sich eine einzige Zeile Ihres Codes bewegt hätte.
- package.json - erklärt die Absicht: die Bereiche, die Sie akzeptieren, Ihre Skripte, Ihre eigenen Metadaten
- package-lock.json - erklärt das Ergebnis: jede aufgelöste Version, ihre Archiv-URL und ihr Integritätswert
- Folge - npm ci schlägt fehl, wenn beide auseinandergehen, und genau das ist der Zweck
Bei einer Bibliothek lautet die Antwort gleich, der Grund ist aber ein anderer, und genau das wird am häufigsten falsch gelesen. Die Lockfile einer veröffentlichten Bibliothek wird von denen, die sie installieren, nicht verwendet — npm ignoriert die Lockfiles von Abhängigkeiten und löst den gesamten Baum aus deren Manifesten auf. Einchecken lohnt sich trotzdem, weil es Ihre eigenen Mitwirkenden und Ihre eigene CI reproduzierbar macht. Ihren Nutzern bringt es schlicht nichts.
package-lock.json in die .gitignore zu setzen hat daher genau einen vertretbaren Zweck: ein Repository, das bewusst bei jedem Lauf die neuesten zulässigen Versionen auflösen will, um früh zu bemerken, wenn eine Veröffentlichung stromaufwärts es bricht. Das ist eine bewusst eingegangene Abwägung mit bekannten Kosten. Es ist keine Voreinstellung.
npm install und npm ci sind nicht derselbe Befehl
npm install liest das Manifest, kann die Lockfile aktualisieren und schreibt klaglos neue Einträge hinein. npm ci liest die Lockfile, weigert sich, sie zu ändern, entfernt node_modules vor der Installation und bricht mit einem Fehler ab, wenn Lockfile und Manifest auseinandergehen. In der Continuous Integration wollen Sie das Zweite, und der Fehler ist das Feature: Eine Abweichung bedeutet, dass ein Bereich geändert wurde, ohne die Sperre neu zu erzeugen.
Deshalb verdient auch ein Pull Request, der package.json anfasst und package-lock.json unberührt lässt, einen zweiten Blick. Entweder wurde das Manifest von Hand bearbeitet, oder die Lockfile wurde neu erzeugt und nie zum Commit hinzugefügt. Beides hinterlässt das Repository in einem Zustand, in dem npm ci für alle anderen fehlschlägt.
Wenn die beiden wirklich auseinandergehen, besteht die Reparatur nicht darin, die Lockfile von Hand zu flicken. Führen Sie den Befehl aus, der sie neu erzeugt — npm install, oder npm install --package-lock-only, wenn Sie die Lockfile auffrischen wollen, ohne node_modules neu zu bauen — und checken Sie das Ergebnis ein. Die Lockfile ist ein Build-Ergebnis, das zufällig versioniert wird, und so sollte man sie behandeln.
FAQ
Sollte package-lock.json in Git eingecheckt werden?
Ja, in fast allen Fällen. Bei einer Anwendung ist sie es, die einen Build reproduzierbar macht: Ohne sie löst die CI den Baum bei jedem Lauf neu auf, und ein stromaufwärts veröffentlichter Patch kann verändern, was Sie ausliefern. Bei einer Bibliothek lohnt es sich weiterhin für Ihre eigenen Mitwirkenden und Ihre eigene CI, auch wenn die Installierenden sie nie lesen. Der einzige vertretbare Grund, sie zu ignorieren, ist ein Repository, das bewusst bei jedem Lauf frische Versionen auflösen will, um Brüche stromaufwärts früh zu bemerken.
Was ist der tatsächliche Unterschied zwischen package.json und package-lock.json?
package.json erklärt, was Sie akzeptieren, ausgedrückt als Bereiche, und listet nur Ihre direkten Abhängigkeiten. package-lock.json hält fest, was tatsächlich aufgelöst wurde: eine genaue Version für jedes Paket im Baum, einschließlich transitiver Abhängigkeiten, die Sie nie genannt haben, jeweils mit der Archiv-URL ihrer Herkunft und einem Integritätswert ihres Inhalts.
Kann ich package-lock.json von Hand bearbeiten?
Sie können, und es ist ein verlässlicher Weg zu einer Lockfile, die keiner Auflösung entspricht, die npm durchführen würde. Erzeugen Sie sie stattdessen neu: npm install, oder npm install --package-lock-only, wenn Sie die Lockfile aktualisieren wollen, ohne node_modules neu zu bauen.
Warum schlägt npm ci fehl, während npm install funktioniert?
Weil die beiden Befehle eine Abweichung unterschiedlich behandeln. npm install aktualisiert die Lockfile stillschweigend, damit sie zum Manifest passt; npm ci weigert sich, beide zu versöhnen, und bricht ab. Ein fehlschlagendes npm ci bedeutet fast immer, dass package.json geändert wurde, ohne die Sperre neu zu erzeugen — genau die Situation, für deren Erkennung es existiert.
Wirkt sich die Lockfile eines Pakets, das ich installiere, auf mein Projekt aus?
Nein. npm ignoriert die Lockfiles Ihrer Abhängigkeiten und löst den gesamten Baum aus deren Manifesten auf. Gelesen wird nur die Lockfile im Wurzelverzeichnis Ihres eigenen Projekts, weshalb die eingecheckte Lockfile einer Bibliothek eher ihren Betreuern als ihren Nutzern dient.



package-lock.json in die .gitignore zu setzen hat daher genau einen vertretbaren Zweck: ein Repository, das bewusst bei jedem Lauf die neuesten zulässigen Versionen auflösen will, um früh zu bemerken, wenn eine Veröffentlichung stromaufwärts es bricht. Das ist eine bewusst eingegangene Abwägung mit bekannten Kosten. Es ist keine Voreinstellung.