Zirkuläre Abhängigkeiten mit SDP verhindern
Zirkuläre Abhängigkeiten sind stille Killer. Sie verursachen keine Build-Fehler. Sie verursachen auch keine Laufzeitfehler beim Import. Stattdessen führen sie zu subtilen undefined-Werten, die erst Wochen später in der Produktion auftreten.
Ein Zyklus entsteht, wenn Modul A von B importiert und B wiederum von A importiert.
JavaScript löst diese Importe geräuschlos auf. Wenn Modul A noch geladen wird, während Modul B es anfordert, gibt JavaScript ein leeres Objekt zurück. Ihr Code schlägt erst später fehl, und der Stacktrace zeigt an die falsche Stelle.
Sie können dies mit dependency-cruiser beheben. Dieses Tool scannt Ihre Dateien und bildet Ihren Abhängigkeitsgraphen ab. Es funktioniert mit TypeScript und Monorepos.
Wie man es einrichtet:
- Fügen Sie es Ihren Dev-Dependencies hinzu: yarn add -D dependency-cruiser
- Fügen Sie ein Skript zu Ihrer package.json hinzu: "depcruise": "depcruise packages --config .dependency-cruiser.js"
- Erstellen Sie eine .dependency-cruiser.js-Datei, um Zyklen zu erkennen.
Doch einen Zyklus zu finden, ist nur die halbe Miete. Sie müssen verhindern, dass sie überhaupt entstehen.
Nutzen Sie das Stable Dependencies Principle (SDP). Diese Regel besagt, dass Abhängigkeiten in Richtung der Stabilität verlaufen müssen.
Stabilität ist eine strukturelle Eigenschaft. Ein Modul ist stabil, wenn viele andere Module von ihm abhängen. Wenn Sie ein stabiles Modul ändern, beeinflussen Sie viele andere. Diese Kosten machen es stabil.
Nutzen Sie die Instabilitäts-Formel: I = Fan-Out / (Fan-In + Fan-Out)
• I = 0 bedeutet, dass ein Modul maximal stabil ist. Alles hängt von ihm ab, und es hängt von nichts ab. • I = 1 bedeutet, dass ein Modul maximal instabil ist. Es hängt von vielen Dingen ab, und nichts hängt von ihm ab.
Die Regel: Der Instabilitätswert eines Moduls muss höher sein als der Wert jedes Moduls, das es importiert. Ihre Pfeile müssen in Richtung geringerer Instabilität zeigen.
Ihre Low-Level-Utils (I ≈ 0) sollten niemals von Ihren High-Level-Pages (I ≈ 1) importieren.
Sie können dieses Prinzip mit dependency-cruiser in eine automatisierte Regel umwandeln. Anstatt nur nach Zyklen zu suchen, prüfen Sie die Richtung:
- Verhindern Sie, dass Utils von Features importieren.
- Verhindern Sie, dass Features von Pages importieren.
Dies stoppt die strukturellen Bedingungen, die Zyklen erzeugen. Sie hören auf, nur auf Bugs zu reagieren, und beginnen, sie zu verhindern.
