Fundamentals 23 min read

Why Your Monorepo Is Slowing Down and How pnpm & Rush Can Fix It

This article examines the scalability and reliability problems of a Yarn‑workspace based monorepo—such as command inconsistency, slow publishing, phantom dependencies, duplicate packages, and lockfile conflicts—and presents pnpm and Rush as comprehensive solutions with practical guidelines for package referencing and workspace protocols.

ELab Team
ELab Team
ELab Team
Why Your Monorepo Is Slowing Down and How pnpm & Rush Can Fix It

Introduction

The team manages all business projects with a monorepo using Yarn workspaces and Lerna, but as the number of projects grows, stability and developer experience suffer, revealing that the current architecture can no longer support the expanding codebase.

Existing Problems

Inconsistent Commands

Three commands (yarn, yarn workspace, lerna) overlap, confusing newcomers.

Slow Publish Speed

Publishing an app installs all dependencies of every app and package, and builds all packages instead of only those required by the target app.

Phantom Dependencies

A library that uses a package not listed in its dependencies creates a phantom (implicit) dependency, which is amplified in the current monorepo architecture.

Version uncertainty of phantom dependencies can cause runtime bugs because the actual version of a transitive dependency is determined by the consuming app’s developer.

NPM Doppelganger

Identical packages may be installed multiple times, leading to duplicate bundles and potential singleton conflicts.

Yarn Duplicate

Yarn duplicate and its solutions are discussed.

Yarn v1 may install a different version of a package than npm, causing duplication.

peerDependencies Risks

Dependency hoisting can introduce bugs when peerDependencies are not satisfied.

app1 depends on [email protected]

app2 depends on [email protected]

[email protected] lists [email protected] as a peerDependency, so app2 should install [email protected].

--apps<br/>    --app1<br/>    --app2<br/>--node_modules<br/>    [email protected]<br/>    [email protected]<br/>

Without the correct version, [email protected] may incorrectly reference [email protected].

Missing Package Reference Standards

Three current reference methods:

Source reference via package name (requires host project build configuration).

Source reference via file path (directly includes source files, risky under hoisting).

Product reference (uses built artifact).

Uncertain Package Version References

When a package is published to npm, how should an app specify its version? The article shows examples with * and explicit versions, and explains Yarn’s resolution steps.

{
  "name": "package1",
  "version": "1.0.0"
}
{
  "name": "app1",
  "dependencies": {
    "package-1": "?" // should this be * or 1.0.0?
  }
}

Yarn first checks for a matching local version, links it if found, otherwise pulls from the remote registry.

Note: * cannot match prerelease versions (see issue #6719).

yarn.lock Conflicts

pnpm automates lockfile conflict resolution, while Yarn requires manual handling, often leading to Git binary conflicts.

Solution

pnpm

Fast, disk‑space‑efficient package manager.

pnpm stores packages in a non‑flattened node_modules structure with symlinks, avoiding deep nesting and duplicate copies.

node_modules<br/>└─ foo<br/>   ├─ index.js<br/>   └─ package.json<br/>└─ bar<br/>   ├─ index.js<br/>   └─ package.json<br/>

Benefits:

Eliminates phantom dependencies by isolating each package’s own dependencies.

Deduplicates identical versions via symlinks, reducing bundle size.

Rush

A scalable monorepo manager for the web.

Key advantages:

Unified commands (e.g., rush install, rush build -t @monorepo/app1).

Powerful dependency analysis (e.g., rush install -t @monorepo/app1 installs only needed deps).

Ensures version consistency via rush check, rush add -p, and ensureConsistentVersions.

Package Reference Guidelines

Three reference styles:

Product reference : app consumes built artifact; fast but hot‑update may be slow.

Source reference : use main field to point to source; fast hot‑updates but requires host project adaptation.

Internal features should not be published externally and should use source reference; external packages must have a build step.

Rush build supports caching; with fine‑grained apps and reusable packages, incremental builds are possible.

Workspace Protocol

Before pnpm and Yarn workspaces, Rush linked all packages into a common folder, effectively providing the same workspace behavior.

Enabling PNPM workspace allows using workspace: ranges (e.g., workspace:*) to guarantee local version resolution.

{
  "dependencies": {
    "foo": "workspace:*",
    "bar": "workspace:~",
    "qar": "workspace:^",
    "zoo": "workspace:^1.5.0"
  }
}

Problem Records

Monorepo Project Dependencies Duplicate

Similar to Yarn duplicate but occurs in any monorepo. Different apps may install different versions of the same library, leading to multiple copies in the final bundle.

{
  "name": "fake-project",
  "version": "1.0.0",
  "dependencies": {
    "@fake-project/app1": "1.0.0",
    "@fake-project/package1": "1.0.0"
  }
}

Solution: use Rush preferredVersions to force a single version, or Yarn‑specific tools like yarn-deduplicate or resolutions.

prettier & eslint

Each project installs its own prettier and eslint as devDependencies, avoiding global installations. Configuration files ( .prettierrc.js, .eslintrc.js) are placed in each package.

git hooks

Use husky ‑like hooks via Rush autoinstallers. Example scripts for commit-msg and pre-commit invoke Rush commands that run commitlint and eslint respectively.

#!/bin/sh
node common/scripts/install-run-rush.js commitlint || exit $?
#!/bin/sh
node common/scripts/install-run-rush.js lint || exit $?

Appendix

Common Commands

Key Yarn vs. Rush command mapping (install, upgrade, add, build, etc.) is provided for quick reference.

Workflow

# Pull latest changes
$ git pull

# Update NPM dependencies
$ rush update

# Rebuild dependencies of @monorepo/app1 (excluding the app itself)
$ rush rebuild -T @monorepo/app1

# Enter the app directory and start it
$ cd ./apps/app1
$ rushx start # or rushx dev

References

Links to official documentation for Yarn, pnpm, Rush, and related issues are listed.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

MonorepopnpmYARNpackage managementrushdependency-issues
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.