All notes
MonoreposAugust 8, 20268 min read

Dependency updates in monorepos

A monorepo adds coordination overhead to every dependency decision. The right conventions keep small updates small and prevent the repo from becoming one giant risk.

Updating dependencies is already a balancing act between staying current and introducing risk. A monorepo multiplies that tension: a single package update can cascade across dozens of workspaces, and a runaway lockfile diff can bury the actual change in noise.

The npm CLI added workspace support in version 7 (2020), pnpm had it from version 4, and Yarn introduced workspaces in Yarn 1 (2017). Before that, monorepos were stitched together with Lerna, symlinks, or ad-hoc scripts. The shift to single-lockfile workspaces was the single most impactful change for monorepo maintainers. One lockfile in the root means one resolved dependency graph, one install command, and one source of truth.

1. Centralized vs package-local manifests

A monorepo always has at least two package.json files: the root manifest and one per workspace. The root file typically holds shareddevDependencies, scripts, and configuration for tools like TypeScript, ESLint, and Prettier. Each workspace file owns its own runtime dependencies and internal package references.

Keep shared tooling in the root. It avoids version drift between workspaces and makes upgrades a single change. Workspace-level manifests should list only the dependencies that workspace actually imports at runtime or needs for its own build. A common mistake is hoisting everything to the root, which defeats the purpose of workspace isolation and makes it hard to tell which package depends on what.

A useful ruleIf a dependency is used by one workspace, declare it there. If several workspaces share a tool, centralize the version policy where your package manager supports it, but keep each workspace explicit about what it imports.

2. Understand the workspace protocol

Workspace references are not identical across package managers, so choose the syntax that matches the tool that owns your lockfile:

Yarn and pnpm use workspace:* in the version field. npm workspaces instead link local packages that are included by the workspace configuration, so keep the declared version compatible with the package's own version. pnpm usesworkspace:^, workspace:~, or workspace:* to express semver intent toward the local package. On publish, pnpm and Yarn replace their workspace protocol with the actual version from the workspace'spackage.json.

The workspace:* wildcard means "always use the local copy." The range variants (workspace:^, workspace:~) mean "resolve to a version that satisfies this range after publish." For internal packages that should always stay in sync during development, prefer the wildcard.

3. Order dependencies in the manifest

A monorepo workspaces array and every dependency block benefit from a stable convention. Alphabetical ordering by package name makes additions and removals jump out during review. It also reduces merge conflicts when two branches add different packages to the same block.

Several tools automate this: npm pkg fix can normalize package metadata, and sort-package-json can impose a stable field order. Integrating a formatter into a pre-commit hook or a CI lint step keeps the diff clean without manual effort.

4. Pick your update strategy

The naive approach is to open every package.json, bump every version to the latest, reinstall, and hope. In a monorepo this produces a lockfile diff that is nearly impossible to review. A better strategy combines automation with human judgment.

Renovate (from Mend, formerly WhiteSource) is the most mature option. It understands workspaces natively, creates separate PRs per package or per group, respects your preset version policy, and supports packageRules to target specific workspaces. For most teams, Renovate with a sensible config (group devDependencies, separate major updates, pin engines andpeerDependencies) eliminates the toil while keeping each PR reviewable.

Changesets (from the Changesets project, widely used in the Atlassian ecosystem and adopted by many open-source monorepos) focuses on versioning and changelog generation for publishable packages. It handles workspace dependencies intelligently and integrates with GitHub Actions for automated PR creation. For monorepos that publish packages, Changesets plus Renovate is a powerful combination: Renovate bumps the versions, Changesets manages the changelog.

5. Use catalogs and overrides

When a dependency is used across multiple workspaces at different ranges, you risk duplicate installations and inconsistent resolutions. Modern package managers provide mechanisms to centralize these decisions.

pnpm catalogs (introduced in pnpm 9) allow you to declare a set of dependency versions in the root pnpm-workspace.yaml and reference them by name from any workspace. This prevents drift and makes a version bump in one place propagate everywhere.

npm overrides and Yarn resolutions solve a related problem: forcing a specific version of a transitive dependency across the entire dependency graph. These are essential when a security advisory requires you to pin a deep dependency without waiting for each intermediate package to release its own update.

A practical workflow for using overrides in a monorepo:

Run npm ls <package> or pnpm why <package>to identify all paths where the vulnerable version appears. Add the override in the root manifest, regenerate the lockfile, and verify with the same command.

6. Test focused, update small

The biggest trap in monorepo dependency management is the monolithic update PR that touches every workspace at once. When tests fail, you have no idea which dependency caused it. The fix is to batch updates into small, reversible units.

A good heuristic: one PR per dependency group. Group by risk profile — patch updates for devDependencies are safe to batch together; major versions each get their own PR. Run the affected workspace's tests first, then the full suite. If a workspace has no tests, consider whether the dependency is pulling its weight.

Focused testing matters here more than anywhere else. Use pnpm's--filter, npm's --workspace, or Yarn'sworkspace command to run test suites against only the workspaces that changed. This keeps CI fast and feedback tight.

Examplespnpm --filter "@scope/{pkg-a,pkg-b}" test runs tests only for two specific workspaces. npm test --workspace=packages/pkg-a --workspace=packages/pkg-b does the same. Yarn Berry accepts yarn workspace @scope/pkg-a test.

7. Lockfile ownership and review

The single lockfile (whether package-lock.json,pnpm-lock.yaml, or yarn.lock) is the most frequently changing file in a monorepo and the hardest to review. Treat it as a generated artifact: never edit it by hand, always regenerate it with the package manager's own command.

For review purposes, pnpm-lock.yaml has an advantage over the npm equivalent — it is YAML-based and less verbose, making diffs more readable. If your team struggles with lockfile review noise, review the manifest and lockfile together, then use each package manager's dependency-tree command to confirm the intended versions. Some teams also run a lockfile-lint step in CI to flag unauthorized changes.

Summary

Monorepos demand more structure around dependency management, not less. Centralize shared tooling in the root, use the workspace protocol where your package manager supports it, automate routine bumps with Renovate, batch changes by risk, and keep the lockfile reviewable. The principle is the same as updating a single project, but the consequences of sloppiness are multiplied by every workspace in the graph.

Sources and further reading