Stop Circular Dependencies With SDP

Circular dependencies are silent killers. They do not cause build errors. They do not cause runtime exceptions on import. Instead, they cause subtle undefined values that appear in production weeks later.

A cycle happens when Module A imports from B, and B imports back from A.

JavaScript resolves these imports silently. If Module A is still loading when Module B requests it, JavaScript returns an empty object. Your code fails later, and the stack trace points to the wrong place.

You can fix this with dependency-cruiser. This tool scans your files and maps your dependency graph. It works with TypeScript and monorepos.

How to set it up:

  • Add it to your dev dependencies: yarn add -D dependency-cruiser
  • Add a script to your package.json: "depcruise": "depcruise packages --config .dependency-cruiser.js"
  • Create a .dependency-cruiser.js file to catch cycles.

But finding a cycle is only half the battle. You must stop them from forming.

Use the Stable Dependencies Principle (SDP). This rule says you must depend in the direction of stability.

Stability is a structural property. A module is stable when many other modules depend on it. If you change a stable module, you affect many people. This cost makes it stable.

Use the Instability formula: I = Fan-Out / (Fan-In + Fan-Out)

• I = 0 means a module is maximally stable. Everything depends on it, and it depends on nothing. • I = 1 means a module is maximally unstable. It depends on many things, and nothing depends on it.

The rule: A module's instability score must be higher than the score of every module it imports. Your arrows must point toward lower instability.

Your low-level utils (I ≈ 0) should never import from your high-level pages (I ≈ 1).

You can turn this principle into an automated rule with dependency-cruiser. Instead of just checking for cycles, check for direction:

  • Prevent utils from importing from features.
  • Prevent features from importing from pages.

This stops the structural conditions that create cycles. You stop reacting to bugs and start preventing them.

Source: https://dev.to/wojciech_kot_b82f5d7cbfc6/stop-circular-dependencies-before-they-stop-you-dependency-cruiser-the-stable-dependencies-34ho