Dependency resolution algorithm
How package managers resolve and download nested dependencies.
Every developer has run npm install and watched it churn through hundreds of packages in seconds. Fewer know the algorithm behind it. This template maps that algorithm: how the package manager discovers what you need, fetches packages and their dependencies, handles version conflicts, and backtracks when no single version satisfies the whole tree. It's the state machine running inside npm, yarn, and pnpm.
Version conflict resolution is where the real work happens. When two direct dependencies want incompatible versions of the same transitive dependency, the manager must either find a version that satisfies both, use nested installs (duplicate the package), or backtrack and try a different version of one of the original packages. Understanding this state machine explains why lockfiles exist, why dependency updates sometimes break things, and why private npm registries with version pinning are so much simpler.
When to use this template
- Onboarding new developers — show your team how npm resolves dependencies, especially when discussing lockfile conflicts or why "just delete node_modules and reinstall" sometimes helps.
- Explaining lockfile strategy — walk through this diagram to show why you commit lockfiles to git and what npm install actually does (hint: it's a replay, not re-resolution).
- Debugging version conflicts — when npm says "multiple versions of X in the tree", this diagram shows why that happens and what resolution backtracking looks like.
How to adapt it
Customize the states and edges to match your specific package manager or internal registry policy:
- Add a registry fallback between "Fetch" and "Check cache" if you use private npm registries and mirror public packages.
- Insert a version pinning policy state after "Fetch" if your organization pins transitive dependencies in a meta-lockfile.
- Add an alert state after "Backtrack" if you want to flag problematic version conflicts or suggest manual interventions.
Visual edits regenerate clean Mermaid code, so you can adjust the flow to match your team's actual resolution and override policies without syntax overhead.
Mermaid code
Copy it anywhere Mermaid is supported — GitHub, Notion, or your docs.
stateDiagram-v2
[*] --> Load
Load: Load package.json
Load --> Resolve
Resolve: Resolve direct dependencies
Resolve --> Queue
Queue: Add to resolution queue
Queue --> Fetch
Fetch: Fetch package metadata
Fetch --> Check
Check: Check cache
Check --> Cached: Found
Check --> NotCached: Not found
Cached: Use cached version
NotCached: Download package
Cached --> Conflict
NotCached --> Conflict
Conflict: Check for version conflicts
Conflict --> HasConflict: Conflict detected
Conflict --> NoConflict: No conflict
HasConflict: Backtrack & try alternative
HasConflict --> Resolve
NoConflict: Mark as resolved
NoConflict --> HasMore: More deps?
HasMore --> Resolve
HasMore --> Installed: Done
Installed: Install resolved tree
Installed --> [*]
Frequently asked questions
- What is dependency resolution?
- It's the process your package manager (npm, yarn, pnpm) runs to find every package you need, including nested dependencies, and download them into node_modules. The resolution must handle version conflicts — if two packages want different versions of the same dependency, the manager must find a compatible version or backtrack and try alternatives. This is what happens when you run npm install.
- Why do lockfiles exist?
- Dependency resolution is expensive and can produce different results on different days as new package versions are published. A lockfile (package-lock.json or yarn.lock) records the exact resolved tree so that running npm install on a different machine or day downloads the same versions. This ensures reproducible builds across your team and CI.
- What is version conflict resolution?
- When two packages require different versions of the same dependency, the package manager tries to find a single version that satisfies both (e.g., both want >=1.0.0 <2.0.0). If none exists, it uses nested installs (two copies of the package in different places) or backtracks to try a different version of one of the requesting packages. This trade-off between efficiency and compatibility is why resolution algorithms are complex.
- How can I visualize my actual dependency tree?
- Run npm ls to see the resolved tree in your node_modules. For a visual graph, use npm ls --depth=0 for direct dependencies only, or tools like depcheck, npm-check, or webpack-bundle-analyzer. This diagram shows the *algorithm* npm runs to build that tree in the first place — adapting it is rarely needed, but understanding it helps debug resolution issues and lockfile conflicts.