Front-end-Entropie ist real. Eine Codebasis bricht nicht über Nacht zusammen. Sie summiert sich auf. An einem Dienstag fügst du eine Bibliothek zur Datumsformatierung hinzu. Sechs Monate später fügt jemand eine weitere hinzu, weil er die erste nicht finden konnte. Polyfills häufen sich für Browser an, die ihr gar nicht mehr unterstützt. Build-Tools schichten sich übereinander. Schließlich wird der node_modules-Ordner zu einer digitalen Rumpelkammer, in der man nichts mehr wegwerfen kann, ohne Angst zu haben. Man hört auf, Updates zu machen. Dann hört man auf, überhaupt hinzusehen. Das ist der Moment, in dem jede kleine Änderung zu einem Glücksspiel wird.
Ich bin gegen diese Wand gestoßen, als ich versuchte, Material UI in einem alten Projekt zu aktualisieren. Ich öffnete die package.json und erkannte kaum die Hälfte der Einträge wieder. Dutzende von Bibliotheken lagen dort, einige Jahre veraltet, andere so obskur, dass ich git blame nutzen musste, um herauszufinden, wer sie hinzugefügt hatte und warum. Ich führte den Install-Befehl für die neue Material-UI-Version aus, und das Terminal leuchtete vor Peer-Dependency-Warnungen auf. Das Paket, das ich aktualisieren wollte, war in Ordnung. Das Ökosystem darum herum jedoch nicht. Mir wurde klar, dass ich kein Upgrade durchführte. Ich betrieb Ausgrabungen in einer Ruine.
Warum das Chaos teurer ist als der Stolz
Abhängigkeiten zu ignorieren, ist kein kosmetisches Problem. Es verursacht echte, teure Probleme.
Sicherheitsrisiken sind die offensichtliche Gefahr. Verlassene Pakete enthalten bekannte Schwachstellen, die wöchentlich von Scannern gemeldet werden. Schlimmer noch: Die Bibliotheken, die du direkt installiert hast, mögen in Ordnung sein, während die transitiven Abhängigkeiten, die sie mitbringen, es nicht sind. Du erbst die technischen Schulden von jemand anderem, ohne es zu wissen.
Kosten steigen exponentiell mit der Zeit. Je länger man wartet, desto größer wird die Versionslücke. Ein Sprung über eine Major-Version von React ist Arbeit. Ein Sprung über drei ist ein Migrationsprojekt, das Wochen verschlingen kann. Man erhält keine Bugfixes, Performance-Verbesserungen oder Kompatibilität mit modernen Tools mehr. Das Team baut am Ende um Einschränkungen herum, die längst nicht mehr existieren sollten.
Bibliotheken sterben. Ein Paket ohne aktive Maintainer wird standardmäßig zu deinem privaten Fork. Wenn es kaputtgeht, bist du derjenige, der um Mitternacht dessen minifizierten Quellcode liest. Die Community ist längst zu besseren Lösungen übergegangen, und dein Team steckt fest in der Wartung eines Geistes.
Die Entwicklungsgeschwindigkeit bricht ein. Neue Entwickler verbringen ihre ersten Tage damit, idiosynkratische APIs für Tools zu lernen, die längst durch Webstandards oder Mainstream-Alternativen ersetzt wurden. Anstatt Features zu liefern, werden deine Senior-Engineers zu Historikern, die erklären müssen, warum dieses Projekt immer noch einen Task-Runner aus dem Jahr 2015 verwendet.
Audit durchführen, bevor man eine einzige Version anfasst
Der schlimmste Fehler ist es, ein pauschales Update durchzuführen und zu hoffen, dass die Tests bestehen. Beginne mit einem Audit. Nimm die package.json zur Hand und hinterfrage jeden einzelnen Eintrag.
Stelle vier Fragen:
- Welches Problem löst das?
- Wo genau verwenden wir es?
- Ist es noch notwendig?
- Gibt es mittlerweile eine bessere Alternative?
Du wirst Redundanzen finden. Vielleicht belegen moment und date-fns beide den Platz in der Liste, weil zwei Entwickler zur unterschiedlichen Zeit dasselbe Problem gelöst haben. Vielleicht wird ein Polyfill für den Internet Explorer immer noch mitgeliefert, obwohl deine Analytics zeigen, dass es null Traffic von Legacy-Browsern gibt. Vielleicht kann ein Custom-Wrapper um fetch gelöscht werden, weil moderne Browser die Edge-Cases nativ handhaben.
Manchmal ist ein Austausch besser als ein Update. Eine verlassene Charting-Bibliothek durch drei Jahre voller Breaking Changes zu schleifen, kann länger dauern, als eine stabile Alternative einzuführen und ein paar Komponenten neu zu bauen. Sei bereit, Dinge zu entfernen.
Die verborgene Ebene: Transitive Abhängigkeiten und Semver
Direkte Abhängigkeiten sind nur der sichtbare Teil des Eisbergs. Der eigentliche Großteil liegt darunter in den transitiven Abhängigkeiten – den Paketen, die deine Pakete benötigen. Du hast sie nicht ausgewählt, aber sie werden in deinem Build ausgeführt. Sie blähen dein Bundle auf, vergrößern die Angriffsfläche und führen gelegentlich zu kryptischen Build-Fehlern durch Konflikte untereinander.
Du musst Semantic Versioning so lesen, wie es tatsächlich gemeint ist, und nicht so, wie du es dir erhoffst.
- Major-Updates: Das sind Migrationen. Behandle sie als Breaking Changes, bis das Gegenteil bewiesen ist. Lies das Changelog, plane Zeit ein und teste gründlich.
- Minor-Updates: Diese fügen Funktionen hinzu. Sie können auch das Verhalten auf subtile Weise ändern. Gehe nicht davon aus, dass sie völlig risikofrei sind.
- Patch-Updates: Diese beheben Fehler. Sie sind normalerweise sicher, aber wenn dein Code auf den Fehler angewiesen ist oder wenn der Patch ein internes Detail ändert, das du per Monkey-Patching manipuliert hast, kann es trotzdem zu Problemen kommen.
Die Regeln zu kennen hilft dir, Risiken zu kategorisieren, bevor du irgendetwas anfasst.
Nutze deine Tools wie ein Handwerker
Wenn du Yarn verwendest, verwandeln mehrere integrierte Befehle das Raten in einen Prozess.
Führe zuerst yarn outdated aus. Es liefert dir eine Momentaufnahme dessen, was sich verschoben hat...
