⚡ Por qué deberías usar pnpm en vez de npm y migrar hoy mismo
Hay un momento en la vida de todo desarrollador de JavaScript en el que abres el explorador de archivos, ves el tamaño de una carpeta node_modules y piensas que algo no puede estar bien. Pesa más que el resto del proyecto entero. Y tienes quince proyectos. Cada uno con su node_modules clonado, con las mismas dependencias repetidas hasta el infinito.
Eso es npm haciendo lo que sabe hacer: copiar. Copiar la misma versión de la misma librería en cada proyecto, una y otra vez, como si el disco fuera infinito.
No lo es. Y desde que me pasé a pnpm recuperé gigas, recuperé minutos en cada instalación y, de paso, dejé de pelearme con un bug que llevaba años fastidiándome sin que yo supiera ni que existía. Te lo cuento.
El problema real de npm: copia todo, mil veces
npm instala dependencias de forma plana y por copia. Cada proyecto recibe su propia copia física de cada paquete dentro de su node_modules. Si tienes diez proyectos que usan la misma versión de React, tienes diez copias de React en el disco. Diez veces lo mismo.
Multiplícalo por las cientos de dependencias transitivas que arrastra cualquier proyecto moderno y entiendes por qué node_modules se convirtió en el meme del agujero negro más pesado del universo.
Y luego está el tiempo. Cada npm install en un proyecto nuevo se baja y descomprime todo otra vez. En un equipo con varios repos, eso son minutos tirados a la basura cada día. Minutos que pagas tú, con tu café enfriándose mientras la barra de progreso avanza.
Cómo lo resuelve pnpm: un almacén único y hard links
Aquí está la idea que lo cambia todo. pnpm no copia: enlaza.
pnpm guarda cada versión de cada paquete UNA sola vez en un almacén global de tu máquina (el content-addressable store). Cuando un proyecto necesita esa dependencia, pnpm no la copia: crea un hard link desde el node_modules del proyecto hasta el archivo que ya está en el almacén. El archivo físico es el mismo, ocupa espacio una vez, y todos los proyectos apuntan a él.
La documentación oficial lo resume mejor que yo: en vez de guardar 100 copias de una dependencia entre 100 proyectos, pnpm mantiene un único almacén central y enlaza contra él.
¿Y las actualizaciones? Igual de finas. Cita textual de la web de pnpm:
if it has 100 files, and a new version has a change in only one of those files,
pnpm updatewill only add 1 new file to the store, instead of cloning the entire dependency.
Osea: si una librería tiene 100 archivos y la nueva versión solo cambia uno, pnpm añade ese archivo al almacén. Uno. No clona los 100 otra vez. Por eso el ahorro de disco no es un detalle de marketing, es estructural.
La velocidad: los números
El ahorro de disco está bien, pero lo que de verdad notas en el día a día es la velocidad. pnpm hace la instalación en tres fases concurrentes (resolver dependencias, calcular la estructura de carpetas y enlazar archivos) en lugar de esperar a que cada fase termine antes de empezar la siguiente.
Estos son los benchmarks oficiales a fecha de hoy, en el escenario “lots of files”, el más parecido a un proyecto real con muchas dependencias:
| Escenario | npm | pnpm |
|---|---|---|
| Instalación limpia (sin caché ni lockfile) | 27,1 s | 7,5 s |
| Con caché | 7,8 s | 2,9 s |
| Con lockfile | 7,2 s | 6,8 s |
| Con caché + lockfile | 5,2 s | 1,7 s |
El caso más habitual en tu máquina del día a día (caché y lockfile presentes) es el de la última fila: pnpm tarda 1,7 segundos donde npm tarda 5,2. Es ±3 veces más rápido justo en el escenario que más repites. Y en una instalación limpia, la diferencia es de 27 segundos a 7 y medio. No es sutil.
El bug silencioso: phantom dependencies
Esta es la parte que más me gustó, y la que casi nadie cuenta cuando habla de pnpm.
El node_modules plano de npm tiene un efecto secundario peligroso: como TODAS las dependencias (las tuyas y las de tus dependencias) acaban aplanadas en la raíz de node_modules, tu código puede hacer import de un paquete que tú nunca instalaste, solo porque resulta que es una dependencia de una dependencia. Funciona. En tu máquina.
Hasta que esa dependencia transitiva cambia de versión, deja de incluir el paquete fantasma, y tu código revienta en producción sin que hayas tocado nada. A eso se le llama phantom dependency, y es de los bugs más frustrantes que existen porque no sale en ningún sitio hasta que explota.
pnpm lo mata de raíz. Su node_modules no es plano: usa symlinks para poner en la raíz solo tus dependencias directas, las que declaraste tú en el package.json. Si tu código intenta importar algo que no declaraste, falla al momento, en tu máquina, no en producción tres meses después. Te obliga a ser honesto con tus dependencias. Y eso, en un proyecto serio, vale oro.
Si te preocupa la compatibilidad de los symlinks con alguna herramienta vieja, pnpm permite volver a una estructura hoisted con un flag de configuración. Tienes la red de seguridad, pero por defecto te protege.
”Pero es que ya tengo npm metido en el dedo”
El argumento real por el que mucha gente no se cambia no es técnico. Es la memoria muscular. Llevas años escribiendo npm install, npm run dev, npm install, npm install… y el dedo va solo.
La buena noticia: no tienes que reeducar el dedo. Puedes hacer que cada vez que escribas npm se ejecute pnpm por debajo, con un alias. Y como esto depende del sistema operativo y del shell que uses, dejé un gist con los aliases para todos los SOs preparados para copiar y pegar.
Lo tienes aquí: npm → pnpm en todos los sistemas operativos. Cubre bash y zsh (Linux y macOS), fish, PowerShell (Windows) y cmd con doskey. Cada archivo dice exactamente dónde pegarlo. Y sí, también funciona dentro de terminales como Warp o iTerm2, porque esos no son shells: ejecutan tu zsh o tu bash de siempre y leen sus archivos de configuración. El alias va en el shell, no en el emulador.
El de bash/zsh, por ejemplo, es tan simple como esto:
if command -v pnpm >/dev/null 2>&1; then
alias npm='pnpm'
fi
La condición está ahí para que, si algún día desinstalas pnpm, no se te quede npm roto apuntando a un comando que no existe.
Antes de migrar: dos avisos honestos
No te voy a vender que es perfecto, porque no lo es y prefiero que lo sepas antes.
Primero: pnpm no es un clon 1:1 de npm. La mayoría de comandos coinciden (install, add, run, update), y pnpm dlx hace lo de npx. Pero alguno cambia: el típico npm ci de los pipelines de CI se traduce a pnpm install --frozen-lockfile. Si pones el alias a lo bruto, ten esto en la cabeza para los scripts.
Segundo: la instalación. La forma limpia de tener pnpm es con Corepack, que viene incluido con Node.js desde la versión 16.13:
corepack enable pnpm
Y a partir de ahí, en cualquier proyecto, pnpm import te convierte el package-lock.json de npm a un pnpm-lock.yaml sin perder las versiones exactas que ya tenías clavadas. La migración de un proyecto existente es cuestión de minutos, no de una tarde.
Si en tu empresa estáis montando o reescribiendo herramientas de desarrollo internas y os interesa hacer este tipo de cosas bien desde la base, puedo echaros una mano.
Entonces, ¿me cambio o no?
Si trabajas con varios proyectos de Node.js, la respuesta es sí, sin pensarlo mucho: recuperas disco, recuperas tiempo y te quitas de encima las phantom dependencies casi gratis. Si tienes un único proyecto pequeño y npm no te molesta, el beneficio existe igual pero lo notarás menos.
En mi caso, la suma de las tres cosas (disco, velocidad e imports honestos) hizo que no haya vuelto a abrir npm de forma consciente. El alias se encarga del resto.
Fuentes
- pnpm — Motivation (almacén content-addressable y hard links)
- pnpm — Benchmarks (11 de junio de 2026)
- Node.js — Corepack
Compártelo si te ha resultado útil.
¿Tú ya te pasaste a pnpm o sigues peleándote con node_modules gigantes? Cuéntame qué te frena.
Y… si lo pruebas hoy, ya me dirás cuántos gigas recuperaste.
Artículos relacionados
Apispain: los datos públicos de España en una API para devs
Necesitaba sacar datos del BORME por código y mi primer plan era montar un scraper. Hasta que me topé con Apispain, que unifica BOE, BORME, BDNS y PLACE en una sola API REST con JSON normalizado, SDK de npm, búsqueda semántica con Inteligencia Artificial y webhooks. Te cuento qué hace, cuánto cuesta, en qué se diferencia de eInforma o Axesor y cuándo merece la pena pagarla en vez de parsear tú las fuentes oficiales.
Scrapling: el scraper de Python que se repara cuando la web cambia
Scrapling es una librería de scraping para Python con una idea que llevábamos años pidiendo: cuando la web cambia su HTML, el scraper se recoloca solo en vez de romperse. Tres fetchers, bypass de Cloudflare, un parser 784 veces más rápido que BeautifulSoup y un framework de spiders incluido. Te cuento qué hace bien, dónde tiene truco y cuándo NO deberías usarlo.
Dograh, Retell o VAPI: cuál elegir para tu agente de voz en 2026
Me llega un proyecto con un requisito que cambia todo: los datos son sensibles y las conversaciones no pueden salir de la infraestructura del cliente. Así fue como acabé comparando en serio Dograh, Retell y VAPI. Precios reales, lo que te cuesta cada opción cuando el volumen crece, y por qué la privacidad es la pregunta que casi nadie hace cuando elige plataforma.