The Module Resolution Hell That Broke Our Last Deploy

Build Performance Degradation

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.

Dependency Graph Chaos

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.

Which Tool Won't Break Your Deploy at 4:30 PM on Friday

Tool

Caching Works?

Will Break on Windows?

Setup Time

Pain Level

PNPM

No but dependencies actually resolve

Nope, just works

2 hours

Boring (good thing)

Turborepo

Yes, when it feels like it

Path limits will hurt you

Weekend

Mostly fine

Nx

Incredible when daemon doesn't crash

Daemon crashes Tuesday afternoons

2 weeks of hell

Worth it eventually

Lerna

Better than old Lerna

Works everywhere

Few days

Solid choice

Rush

Probably?

Microsoft made it so probably

Month minimum

Don't

Why Turborepo's Cache Keeps Lying to Me

Tool Comparison Matrix

I've been debugging the same Turborepo caching issue for 3 weeks now. It's driving me insane. The cache says nothing changed, serves me stale builds, and my API endpoints throw runtime errors because they're using old TypeScript interfaces.

Here's what happens: I change a function signature in our shared utils package. TypeScript compiles fine. Tests pass locally. Turborepo says "cache hit!" and deploys the old version of dependent packages. Production APIs start throwing Property 'newField' does not exist errors because half the services got the new interface and half got cached old builds.

The cache invalidation logic is a fucking black box. Change one tiny thing? Full rebuild. Change something that actually breaks downstream packages? "Cache is fresh, bro!"

The Command That Never Works

`turbo prune` is supposed to fix cache issues. It doesn't. Here's what I've tried:

turbo prune           # "Pruned 0 items"
turbo prune --force   # Still serves stale cache
rm -rf .turbo/cache   # Nuclear option, still cached somewhere

The cache lives in like 5 different places. Local .turbo/cache, some global cache directory, Vercel's remote cache, and probably your browser's local storage at this point. Clearing one doesn't clear the others.

When Cache Hits Are Actually Cache Lies

Last Tuesday I spent 4 hours debugging why our checkout flow was broken in staging. Turned out Turborepo cached a build from 2 days ago when I accidentally reverted a payment processing change. The cache fingerprint somehow matched even though the code was completely different.

The hash algorithm apparently thinks these are identical:

  • Code that processes credit cards correctly
  • Code that was broken 2 days ago

But it rebuilds everything when you change a comment in a README file.

The Turborepo team knows about this but their suggested cache debugging steps don't help when the cache is confidently wrong.

Turborepo Performance
Code Development

The --force Flag Is Your Only Friend

I've started running `turbo build --force` for anything that touches shared interfaces. Defeats the entire purpose of caching but at least my builds work.

Team lead asked why our "fast build tool" takes 15 minutes now. Had to explain that fast builds don't matter if they deploy broken code.

What Actually Works Consistently

PNPM doesn't have caching so it can't lie to you. Builds take longer but they're correct every time. No cache invalidation bugs because there's no cache to invalidate.

Nx has better dependency tracking. When I change a shared interface, it correctly identifies the 12 packages that depend on it and rebuilds all of them. Takes some time to set up but the dependency graph actually works.

The breaking point isn't build speed - it's trust. Can't use a tool that randomly serves stale builds and calls them fresh. Would rather wait 10 minutes for a correct build than get a 30-second build that breaks production.

Fix Your Build When These Tools Break (And They Will)

Q

Yarn Workspaces just stopped resolving dependencies

A

bash# Kill it with fire approachrm -rf node_modules yarn.lockyarn install --check-files# If that doesn't workyarn install --force --network-timeout 100000The --check-files flag forces Yarn to verify every installed file. Takes forever but fixes weird corruption issues. --network-timeout helps with those random network failures that fuck up the install halfway through.

Q

PNPM lockfile is corrupted and nothing installs

A

bash# Nuclear option firstrm pnpm-lock.yaml node_modulespnpm install# If you get ERR_PNPM_LOCKFILE_BREAKING_CHANGEpnpm install --frozen-lockfile=falsePNPM gets pissy about lockfile changes but --frozen-lockfile=false forces it to rebuild. Usually works.

Q

Turborepo cache is serving stale builds

A

bash# Clear everything, this takes foreverturbo prune && rm -rf .turbo/cacherm -rf ~/.local/share/turbo # Linux/Mac global cacherm -rf %APPDATA%/turbo # Windows global cache# Nuclear option when remote cache is fuckedturbo build --forceThe global cache directories are where Turborepo hides its lies. Clearing them usually fixes the "cache hit but wrong build" problem.

Q

Nx daemon crashed and now nothing works

A

```bash# Kill the daemonnx resetrm -rf .nx/cache# If daemon won't startexport NX_DAEMON=falsenx build# Last resortnpx nx@latest reset --verbose````NX_DAEMON=falsedisables the daemon completely. Builds take longer but they actually work. The--verbose` flag shows you exactly why the daemon is dying.

Q

"EACCES: permission denied" during install

A

This usually means your npm/yarn/pnpm global directory has wrong permissions. Don't use sudo to fix it.

bash# Fix npm permissionsmkdir ~/.npm-globalnpm config set prefix '~/.npm-global'export PATH=~/.npm-global/bin:$PATH# Fix pnpm permissions pnpm config set store-dir ~/.pnpm-store

Q

"Cannot resolve module" but the module exists

A

Phantom dependency or hoisting issue. The module exists somewhere but not where the bundler expects it.

bash# Quick fix - add it as a direct dependencynpm install the-missing-module# Proper fix - figure out why it's not hoistedyarn why the-missing-module

Q

Build works locally but fails in CI

A

Usually environment differences or caching issues.

bash# Match your local Node version in CInode --version # copy this to your CI config# Clear CI cacherm -rf ~/.npm ~/.yarn/cache ~/.cache/yarn

Q

Path too long errors

A

Windows path limit is 260 characters. Monorepo paths get long fast.

bash# Enable long paths (requires admin)git config --system core.longpaths true# Or use shorter package names# packages/shared-ui-components -> packages/ui

Q

EPERM errors during builds

A

Windows Defender thinks your build files are viruses.

  1. Open Windows Security
  2. Add exclusion for your project directory
  3. Add exclusion for %TEMP% urbo or %TEMP% x
  4. Restart your terminal
Q

PowerShell vs CMD differences

A

Different shells handle paths differently. Pick one and stick with it.

bash# Use forward slashes everywhere./scripts/build.sh # works in both.\scripts\build.sh # only works in PowerShell

Resources That'll Save Your Ass When Builds Break

Related Tools & Recommendations

compare
Similar content

Nx vs Lerna vs Rush vs Bazel vs Turborepo: Monorepo Tools Compared

Which monorepo tool won't make you hate your life

Nx
/compare/nx/lerna/rush/bazel/turborepo/monorepo-tools-comparison
100%
compare
Similar content

Nx vs Turborepo: Deep Dive into Monorepo Build Performance

After 8 months in monorepo hell, here's what actually works

Nx
/compare/nx/turborepo/build-performance-comparison
55%
tool
Similar content

Turborepo Overview: Optimize Monorepo Builds & Caching

Finally, a build system that doesn't rebuild everything when you change one fucking line

Turborepo
/tool/turborepo/overview
38%
tool
Similar content

Nx Monorepo Overview: Caching, Performance & Setup Guide

Monorepo build tool that actually works when your codebase gets too big to manage

Nx
/tool/nx/overview
26%
review
Recommended

Vite vs Webpack vs Turbopack: Which One Doesn't Suck?

I tested all three on 6 different projects so you don't have to suffer through webpack config hell

Vite
/review/vite-webpack-turbopack/performance-benchmark-review
24%
tool
Recommended

npm Enterprise Troubleshooting - When Corporate IT Meets JavaScript

Production failures, proxy hell, and the CI/CD problems that actually cost money

npm
/tool/npm/enterprise-troubleshooting
18%
troubleshoot
Recommended

npm Permission Errors Are Still a Nightmare

EACCES permission denied errors that make you want to throw your laptop out the window

npm
/troubleshoot/npm-eacces-permission-denied/latest-permission-fixes-2025
18%
troubleshoot
Recommended

npm Permission Errors Are the Worst

compatible with npm

npm
/troubleshoot/npm-eacces-permission-denied/eacces-permission-errors-solutions
18%
review
Recommended

Which JavaScript Runtime Won't Make You Hate Your Life

Two years of runtime fuckery later, here's the truth nobody tells you

Bun
/review/bun-nodejs-deno-comparison/production-readiness-assessment
15%
howto
Recommended

Install Node.js with NVM on Mac M1/M2/M3 - Because Life's Too Short for Version Hell

My M1 Mac setup broke at 2am before a deployment. Here's how I fixed it so you don't have to suffer.

Node Version Manager (NVM)
/howto/install-nodejs-nvm-mac-m1/complete-installation-guide
15%
integration
Recommended

Claude API Code Execution Integration - Advanced Tools Guide

Build production-ready applications with Claude's code execution and file processing tools

Claude API
/integration/claude-api-nodejs-express/advanced-tools-integration
15%
integration
Recommended

Stop Your APIs From Breaking Every Time You Touch The Database

Prisma + tRPC + TypeScript: No More "It Works In Dev" Surprises

Prisma
/integration/prisma-trpc-typescript/full-stack-architecture
15%
tool
Recommended

TypeScript - JavaScript That Catches Your Bugs

Microsoft's type system that catches bugs before they hit production

TypeScript
/tool/typescript/overview
15%
tool
Recommended

JavaScript to TypeScript Migration - Practical Troubleshooting Guide

This guide covers the shit that actually breaks during migration

TypeScript
/tool/typescript/migration-troubleshooting-guide
15%
compare
Similar content

Monorepo Tools vs. Microservices: Scaling Enterprise Builds with Nx

Should you fix it with monorepo tools or split everything into microservices?

/compare/monorepo-tools/enterprise-scaling/enterprise-scaling-comparison
14%
review
Recommended

ESLint + Prettier Setup Review - The Hard Truth About JavaScript's Golden Couple

After 7 years of dominance, the cracks are showing

ESLint
/review/eslint-prettier-setup/performance-usability-review
13%
tool
Recommended

ESLint - Find and Fix Problems in Your JavaScript Code

The pluggable linting utility for JavaScript and JSX

eslint
/tool/eslint/overview
13%
tool
Recommended

Webpack - The Build Tool You'll Love to Hate

compatible with Webpack

Webpack
/tool/webpack/overview
13%
tool
Recommended

Webpack Performance Optimization - Fix Slow Builds and Giant Bundles

compatible with Webpack

Webpack
/tool/webpack/performance-optimization
13%
tool
Recommended

Vite - Build Tool That Doesn't Make You Wait

Dev server that actually starts fast, unlike Webpack

Vite
/tool/vite/overview
13%

Recommendations combine user behavior, content similarity, research intelligence, and SEO optimization