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.
Resolution algorithm
Section titled “Resolution algorithm”Rush uses a greedy, single-pass BFS — the same fundamental strategy as npm, Yarn, pnpm, and Bun.
Principles
Section titled “Principles”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.
Walk-through
Section titled “Walk-through”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 dependencies5. Recurse into each dep...6. Mark all as resolved7. Return resolved graphHoisting and nesting
Section titled “Hoisting and nesting”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.
Dependency types
Section titled “Dependency types”| Type | Installed? | Transitive? | Failure mode |
|---|---|---|---|
dependencies | Always | Yes | Fail-fast |
devDependencies | Root only; skipped with --production | No | Fail-fast |
optionalDependencies | Yes | Yes | Silent skip |
peerDependencies | Auto-installed (npm 7+ style) | Validated | Hard error if unmet |
Store layout
Section titled “Store layout”Rush uses a content-addressed store to avoid redundant downloads and extractions:
~/.rush/store/ ← shared across all projects on your machine├── [email protected]/└── [email protected]/
<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.
Bin linking
Section titled “Bin linking”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.jsTransitive-dependency binaries are not linked (matching npm v5+ behaviour). Use rush exec <binary> to run any installed binary directly.
Lifecycle scripts
Section titled “Lifecycle scripts”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:
rush trust add <package>See rush trust for the full workflow.