Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Bol moved its internal Backstage platform from Yarn 4 to pnpm 12.3.0 to better support parallel development in separate Git worktrees—not because a controlled benchmark showed pnpm was universally faster. The migration’s most useful lessons were operational: verify the store path from inside CI, cache only what helps, and test both clean clones and long-lived checkouts.

Why bol moved its Backstage platform from Yarn to pnpm

In a first-person account published by Bogdan Nechyporenko, a developer at bol, the team’s main motivation was a workflow that uses separate Git worktrees for parallel development. The package content could be reused through pnpm’s shared content-addressable store, while each worktree had its own checkout. This was a fit for bol’s workflow, not evidence that pnpm outperforms Yarn for every repository. Nechyporenko’s account describes the migration as a case study rather than a controlled comparison.

The visible changes included pinning pnpm 12.3.0 in package.json, committing pnpm-lock.yaml, replacing Yarn commands, and updating contributor documentation. The harder work was finding assumptions about dependency paths embedded in CI configuration, container images, startup scripts, and a custom worktree helper.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How the first CI cache miss exposed a path mismatch

On cold GitLab CI runs, the job downloaded all 4,226 packages, according to Nechyporenko. The cause was not simply a missing cache declaration: the image configured the store at /builds/.pnpm-store, while GitLab archived the project-local .pnpm-store. The job was writing its package store outside the path being cached.

The author says explicitly setting the store path and printing its effective value from the running job resolved the mismatch. The practical lesson is to inspect the path the job actually uses, rather than infer it from a configuration file that may be overridden by an image, environment, or CI setting. Nechyporenko’s phrasing was: “That was the first real lesson of this migration: never reason about a package-manager cache from configuration files alone. Ask the running job where its store actually is.”

Why caching every node_modules tree made CI worse

After fixing the store path, bol’s cache included both the pnpm store and every node_modules tree. Four jobs restored a large archive, but the author says dependency layouts and native modules were still rebuilt. In this setup, transferring and extracting those trees added overhead without delivering the expected reuse.

Bol CI observation Reported value
Cache before excluding node_modules 1.42 GB and about 601,000 files, as reported by Bogdan Nechyporenko in 2026
Cache after excluding node_modules About 400 MB and 244,000 files, as reported by Bogdan Nechyporenko in 2026
Estimated pipeline time saved About six minutes per pipeline, the author’s estimate for bol’s setup

pnpm’s CI guidance warns against assuming that store caching will always help: “However, this is not required, and it is not guaranteed that caching the store will make installation faster.” See the pnpm continuous integration documentation. Measure cache restore, install, native builds, and cache saving separately on the actual runner; then decide whether caching the store is worthwhile.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why the worktree helper failed under pnpm

The old worktree bootstrap used a custom symlink farm built around Yarn’s former dependency layout. When it encountered pnpm’s layout, it hit ENOTDIR. pnpm documents a virtual store under node_modules/.pnpm: package files are hard-linked from the content-addressable store, and symlinks form the dependency graph. Those are distinct filesystem relationships, so scripts that assume a particular directory or symlink shape can fail even when installation succeeds. The pnpm explanation of its symlinked node_modules structure describes the layout.

Nechyporenko reports replacing a roughly 370-line helper with pnpm install --frozen-lockfile; the resulting helper was 31 lines. A lightweight verifier checks for node_modules/.pnpm, but that does not prove the application is running code from the intended checkout. The author wanted an integration check confirming that a plugin edited in a secondary worktree is the source loaded by the running Backstage application. That runtime proof matters more than merely finding the expected directory.

What existing checkouts needed that clean clones did not

A clean clone worked, but some developer checkouts retained a project-local .pnpm-store created under an earlier configuration. Nechyporenko says repeated starts could relink roughly 4,800 packages and take about four minutes. The account describes a one-time local startup migration that removed the stale local store, root node_modules, and dependency hash before running a clean install.

Rank #4
The SQL Programming Language: .
  • Used Book in Good Condition

That cleanup was specific to local stale state: bol intentionally kept a project-local store in CI. A migration should therefore distinguish developer-machine cleanup from CI cache policy rather than applying a broad deletion rule to both environments.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Test the two states independently:

  • A fresh clone installing from the committed lockfile.
  • An existing checkout with the old store and dependency state present.
  • An unchanged restart after migration.
  • A restart after changing dependencies.
  • A secondary worktree where an edited plugin is confirmed to be the code the running application uses.

How bol handled slow registry responses

For slower office or VPN connections, the team used a small registry request as a latency probe. If that request succeeded but took more than three seconds, their flow added --network-concurrency=1, five fetch retries, and a longer maximum retry timeout. These were settings in one team’s implementation, not universal pnpm recommendations.

Best Value
Computer Programming For Teens
  • Used Book in Good Condition

The author notes an important edge case: a failed probe skips the slow-but-successful branch. A timeout or authentication failure therefore needs deliberate handling of its own; otherwise, the retry settings may not be applied when connectivity is actually broken.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Which reported improvements were not caused by pnpm alone

The migration account also describes broader CI changes: switching Jest coverage to V8, enabling inline source maps for @swc/jest, using incremental TypeScript compilation, adding native build prerequisites, and setting a keytar build policy. The author reports the Jest cache falling from 5.2 GB to no more than 2 GB, and unit-test time declining from 17 minutes to about 10 minutes. Those are bol-specific observations, and Nechyporenko cautions that test selection, coverage policy, worker limits, and cache changes contributed. They should not be read as pnpm-only savings. Jest’s configuration documentation provides context for its configuration options, not independent verification of these results.

What to check before a similar migration

For another Backstage monorepo, the relevant question is not which package manager wins in the abstract. It is whether the new dependency layout suits the team’s actual development and CI workflow. Before switching, inventory scripts and infrastructure that touch dependencies, then validate the transition in both clean and existing environments.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Worktree reuse: Confirm how separate checkouts share package content and whether the application loads the intended worktree’s code.
  • Store location: Print the effective pnpm store path from the actual CI job and ensure it matches the path being archived.
  • Cache economics: Compare restore and save transfer time with installation and native-build time. Do not assume caching a store or node_modules will reduce total pipeline time.
  • Layout assumptions: Inspect worktree helpers, image setup, startup scripts, generators, and contributor documentation for hard-coded paths or symlink expectations.
  • Checkout history: Exercise both clean clones and existing checkouts with stale local state.
  • Native dependencies: Check which packages need build tools or runtime support in the CI image.

The account’s central outcome is clearer ownership of dependency layout and worktree behavior, not a universal speed claim. Its figures are self-reported measurements from bol’s GitLab CI and developer environment, with no independent head-to-head benchmark against Yarn.

Quick Recap

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.