Last Thursday at 2:30 PM, middle of a hotfix deploy, Yarn Workspaces decided that our authentication package couldn't find bcrypt
anymore. Same code that had been working for 6 months. Same lockfile. Same everything. The error:
Module not found: Can't resolve 'bcrypt' in '/packages/auth/src'
But here's the thing - bcrypt
was right there in our package.json dependencies. VSCode saw it. TypeScript was happy. Even `yarn why bcrypt` showed it installed correctly. But the webpack build? Nope. "Never heard of bcrypt, must be imaginary."
Spent 3 hours figuring out that hoisting had moved bcrypt
into some nested path because we updated a totally unrelated UI package. Yarn's hoisting algorithm saw that update and thought "you know what? Let's reorganize EVERYTHING." No warning. No explanation. Just suddenly our production auth was broken.
The "Phantom Dependency" Game
This is the thing that makes Yarn Workspaces actually dangerous. Your code imports something, your editor confirms it exists, your build passes locally. Then production deploys and everything breaks because hoisting is completely unpredictable.
Package A used to import lodash
from the root. Package B gets updated with a newer lodash
version. Suddenly Package A's import breaks because Yarn decided the root lodash
needed to move. The best part? This only happens sometimes. Like maybe 40% of deployments. The other 60% it works fine, so you never know if you're about to break production.
Our deploy checklist now includes "pray to whatever gods handle dependency resolution" right after the security scan.
The npm hoisting behavior is documented but the Yarn implementation has subtle differences that can break builds. Even Node.js module resolution doesn't guarantee where packages end up.
What Actually Works
PNPM Workspaces solved the phantom dependency problem in about 2 hours. It creates proper isolated node_modules for each package instead of relying on hoisting magic. If a package imports something, it better damn well declare it as a dependency.
Turborepo cut our build times from 18 minutes to 4 minutes with zero configuration. Just works. Cache hits, builds skip. Revolutionary concept.
Nx is the nuclear option. Learning curve from hell but once you get it, builds that used to take 20+ minutes finish in under 2 minutes. Their dependency graph analysis actually knows what changed.
The breaking point for switching isn't subjective - it's when your deploy process includes crossing your fingers that hoisting doesn't break production. That's not engineering, that's gambling.