Skip to content

How It Works

Rush is designed to do the minimum necessary work on every run. This page explains the algorithms and design decisions behind that.


Rush uses a greedy, single-pass BFS — the same fundamental strategy as npm, Yarn, pnpm, and Bun.

No backtracking. Rush picks the newest version satisfying each dependency range and commits to it. If a conflict is detected, it fails immediately rather than searching for an alternative combination.

Greedy selection. The first compatible version wins. Determinism comes from the lockfile — once rush.lock exists, resolution is skipped entirely and exact pinned versions are installed directly.

Fail-fast. On the first unresolvable conflict, Rush exits with a clear error. This matches Bun/npm/pnpm behaviour and keeps CI failures actionable.

Cycle tolerance. Circular dependencies (common with peer deps) are handled gracefully. Rush tracks which packages are currently being resolved and reuses them when a cycle is detected.

User requests: react@^18.0.0
1. Fetch packument for "react"
2. Pick newest matching version: [email protected]
3. Mark as resolving: { react: 18.2.0 }
4. Inspect [email protected]'s dependencies
5. Recurse into each dep...
6. Mark all as resolved
7. Return resolved graph

Rush produces an npm-compatible node_modules structure with hoisting:

  • Hoisted — packages that can be shared (their version satisfies all requirers) are placed at the top level of node_modules/.
  • Nested — when two packages require conflicting versions of the same dep, the conflicting version is nested under the requiring package’s own node_modules/.

This is the same two-level strategy npm v3+ uses.


TypeInstalled?Transitive?Failure mode
dependenciesAlwaysYesFail-fast
devDependenciesRoot only; skipped with --productionNoFail-fast
optionalDependenciesYesYesSilent skip
peerDependenciesAuto-installed (npm 7+ style)ValidatedHard error if unmet

Rush uses a content-addressed store to avoid redundant downloads and extractions:

~/.rush/store/ ← shared across all projects on your machine
<project>/node_modules/
├── .rush/
│ ├── [email protected]/ ← extracted for this project
│ │ └── .rush-integrity ← sentinel: sha512-<hash>
│ └── [email protected]/
├── .bin/ ← symlinked executables
├── express/
└── typescript/

The .rush-integrity sentinel holds the package’s integrity hash. On each install, Rush checks this file first — if it matches, the package is skipped entirely (no download, no extraction, no hash verification).

See Global cache for more on caching behaviour.


Executables declared in a package’s bin field are symlinked into node_modules/.bin/ for direct dependencies only.

node_modules/.bin/
├── tsc → ../.rush/[email protected]/bin/tsc
├── vite → ../.rush/[email protected]/dist/bin/vite.js
└── tailwindcss → ../.rush/[email protected]/lib/cli.js

Transitive-dependency binaries are not linked (matching npm v5+ behaviour). Use rush exec <binary> to run any installed binary directly.


Packages that declare preinstall, install, or postinstall scripts are blocked by default to prevent supply-chain attacks. After each install, Rush reports which packages were blocked.

To allow a package’s scripts to run, add it to trustedDependencies in package.json or run:

Terminal window
rush trust add <package>

See rush trust for the full workflow.