Proliferación de dependencias npm: coste real y cómo reducirla
Un proyecto Next.js recién creado instala más de 1.000 paquetes antes del código. Esto cuesta la proliferación de dependencias npm y así la reducimos.
La proliferación de dependencias npm es el crecimiento sin control de los paquetes que instala un proyecto frontend, tanto los que eliges como los cientos que nunca has visto, y que infla el tiempo de instalación, el peso del bundle y la exposición a riesgos de seguridad sin añadir una sola función. Es la razón por la que un proyecto Next.js recién creado arrastra más de mil paquetes antes de escribir una línea de código, y la razón por la que la carpeta node_modules es el objeto más pesado del repositorio.
Todos los equipos llegan igual. Añades una librería para resolver un problema. Esa librería añade doce suyas. Seis meses después nadie sabe qué hace la mitad del árbol, npm install tarda noventa segundos y llega un aviso de seguridad sobre un paquete a tres niveles de profundidad que nunca decidiste usar. Aquí desglosamos lo que cuesta de verdad esta proliferación y las medidas que usamos para mantenerla a raya.
Qué te cuesta la proliferación de dependencias npm
Los números son peores de lo que muchos equipos suponen. Un proyecto npm medio arrastra unas 79 dependencias transitivas, paquetes que ningún desarrollador auditó de forma directa (Gecko Security). En una app real la cuenta sube rápido: una aplicación React con 50 dependencias directas resuelve normalmente entre 800 y 1.200 transitivas, y un proyecto Next.js parte por encima de las mil antes de que exista el código de la aplicación.
Ese peso es físico. Christoph Nakazawa, antes al frente de los equipos de React Native y Jest, midió la node_modules de su propio sitio: más de 40.000 archivos y más de 570 MiB (cpojer.net). Multiplícalo por cada rama, cada ejecución de CI, cada portátil y cada build en contenedor. El tiempo de instalación, los minutos de CI en frío y el disco crecen todos sobre un árbol que en su mayor parte nunca se llegará a llamar.
Luego está la parte que quita el sueño a los equipos de seguridad. El número de dependencias es superficie de ataque. En 2026, más del 99% de todo el malware de código abierto apunta a npm, lo que lo convierte en el ecosistema de paquetes más atacado del mundo (Sonatype). Las publicaciones de paquetes maliciosos crecieron un 73% en un año (Adyog). Buena parte de ese riesgo entra por los paquetes transitivos, los que no elegiste y ni siquiera sabes nombrar. La versión 12 de npm empezó a bloquear por defecto los scripts de instalación, descritos como la mayor superficie de ejecución de código del ecosistema (TechTimes).
El peso del bundle es el último coste, y el que notan los usuarios. El código muerto en el árbol se convierte en código muerto en el bundle que llega al navegador, que se convierte en cargas más lentas y peores Core Web Vitals. La versión en tiempo de ejecución de este problema la tratamos en los errores de frontend que hunden la puntuación de Lighthouse.
Por qué "basta con lanzar npm audit" no lo arregla
El reflejo es lanzar npm audit, corregir lo que marca y seguir. Eso trata los síntomas. npm audit te dice qué paquetes instalados tienen avisos conocidos. No dice qué paquetes no necesitas, cuáles se duplican entre sí ni qué micro-paquete de una sola función podrían ser cuatro líneas tuyas. Un árbol puede pasar la auditoría limpio y aun así ocupar el doble de lo que debería.
Borrar paquetes a mano tampoco escala. Nadie recuerda por qué se añadió una dependencia, así que se queda por miedo. El resultado es un trinquete: el árbol solo crece. La solución no es una limpieza puntual. Es un conjunto de hábitos que convierten añadir una dependencia en una decisión y no en un reflejo.
Qué controla de verdad la proliferación
Mide el coste real antes de instalar
Casi todas las dependencias se añaden a ciegas. El hábito que lo cambia es comprobar el coste transitivo completo antes de npm install, no después. BundlePhobia muestra el peso real que un paquete añade al bundle, incluido todo lo que arrastra consigo, para que compares dos librerías por coste antes de decidirte por una (BundlePhobia). Un paquete que en el README parece pequeño puede cargar un megabyte de peso transitivo. Conviene saberlo antes de que entre en el lockfile, no durante una revisión de rendimiento.
Encuentra y elimina las dependencias muertas de forma automática
No puedes cortar lo que no ves. Dos herramientas sacan a la luz los paquetes sin uso mediante análisis estático. Knip se ha convertido en el estándar de 2026: soporta más de 50 frameworks, entre ellos Next.js, Vite y Astro, y en una sola pasada detecta archivos, exports y dependencias sin uso (Knip). Depcheck es más acotado, solo marca las entradas sin uso del package.json, pero está probado con 400.000 descargas semanales. Pon uno a correr en CI en cada pull request. Cazar una dependencia de más en la revisión cuesta poco. Encontrarla dos años después es arqueología.
Prefiere la plataforma al micro-paquete
Una buena parte de cualquier árbol son paquetes que duplican lo que la plataforma ya hace sola. Formatear fechas, clonar en profundidad, dividir arrays, generar UUID: todo viene ya en el JavaScript moderno y en el navegador. Cada micro-dependencia que sustituyes por una llamada a la librería estándar elimina no solo ese paquete, sino todo su subárbol. Es la misma lógica detrás de quitar Tailwind de producción y de tratar las librerías de componentes de terceros como un atajo caro: la dependencia que no añades es la que nunca tendrás que auditar, parchear ni empaquetar.
Bloquea el árbol y cierra el agujero de los scripts de instalación
Sube el lockfile al control de versiones, instala con npm ci en CI para que el árbol sea reproducible y, en npm 12 o versiones posteriores, mantén activo el bloqueo de los scripts de instalación. Si un paquete necesita un script de posinstalación para funcionar, es una señal para revisar, no para dejar pasar. Fija las versiones y deja que una herramienta programada como Dependabot o Renovate proponga las actualizaciones en lotes revisables, en lugar de que el árbol se mueva solo.
Trata las dependencias como una partida de gasto
Fija una regla que todo el equipo pueda aplicar: una dependencia nueva necesita una justificación de una línea en la pull request. Qué hace, cuánto cuesta en peso transitivo y por qué escribirla nosotros sería peor. Suena burocrático. Lleva diez segundos y es el hábito de mayor rendimiento para mantener el árbol plano, porque traslada la decisión al único momento en que alguien entiende de verdad el equilibrio.
Cómo se ve en la práctica
En una auditoría reciente de un panel Next.js de tamaño medio el patrón era de manual. La app tenía 61 dependencias directas y algo menos de 1.300 en el árbol resuelto. Knip marcó 14 paquetes directos sin ninguna referencia, restos de funciones que se habían eliminado. Había tres librerías de fechas distintas, instaladas porque tres desarrolladores eligieron cada uno la suya. Dos paquetes de utilidades reimplementaban funciones que el lenguaje ya incluye. Quitar los 14 paquetes muertos y unificar los duplicados llevó una tarde, redujo la instalación en una fracción notable y aligeró el bundle que llega al navegador. Nada de esto exigió reescribir el código de producto. Exigió mirar.
El resultado del propio Nakazawa apunta en la misma dirección: aplicar una poda disciplinada al árbol de React Native redujo en un orden de magnitud el peso de las dependencias de terceros y mejoró los tiempos de instalación en la misma medida (cpojer.net). La proliferación no es inevitable. Es lo que obtienes por defecto cuando nadie vigila el árbol, y se revierte en cuanto alguien lo hace.
Preguntas frecuentes
¿Cuántas dependencias npm son demasiadas?
No hay un número fijo, pero la señal útil es la relación entre dependencias transitivas y directas y cuántas puedes justificar de verdad. Una app React que resuelve entre 800 y 1.200 paquetes transitivos a partir de 50 directos es normal; esas mismas 50 directas arrastrando 3.000 son una alarma. Fíjate en la tendencia más que en el valor absoluto: si el árbol crece cada trimestre y nadie quita nada, el número ya es demasiado alto. Lanza Knip o depcheck y mira cuántos paquetes están sin uso. Cualquier cifra por encima de cero merece un recorte.
¿Quitar dependencias sin uso hace de verdad más rápido un sitio?
Depende de dónde viviera la dependencia. Quitar un paquete que llegaba al bundle del navegador reduce el JavaScript que hay que descargar, interpretar y ejecutar, y eso mejora directamente el tiempo de carga y los Core Web Vitals. Quitar una dependencia solo de build o de desarrollo no cambia el bundle, pero acelera las instalaciones y la CI. En ambos casos reduces la superficie de seguridad. La mejora de rendimiento es mayor cuando el paquete muerto estaba en el camino hacia el navegador.
¿La proliferación de dependencias es un riesgo de seguridad aunque npm audit salga limpio?
Sí. npm audit solo informa de avisos conocidos sobre las versiones instaladas. Un paquete malicioso recién publicado, o uno recién comprometido, pasa la auditoría hasta que se difunde el aviso, algo que puede ocurrir días después del ataque. Cada paquete transitivo es código que se ejecuta con los privilegios de tu app, así que un árbol más grande es una superficie de ataque más grande al margen de lo que diga la auditoría ahora. Por eso npm 12 bloquea los scripts de instalación por defecto y por eso recortar los paquetes sin uso importa tanto como parchear los marcados.
¿Debería cambiar a pnpm o yarn para arreglar el peso de node_modules?
pnpm ataca el síntoma, no la causa. Su almacén direccionable por contenido enlaza con hard links los paquetes compartidos entre proyectos, así que el uso de disco y los tiempos de instalación en frío bajan mucho, un alivio real en una máquina con muchos repositorios. Pero pnpm no elimina los paquetes que no necesitas: el árbol es igual de grande a nivel lógico, solo que se guarda de forma más eficiente. Usa pnpm por la ventaja en disco y velocidad, y sigue lanzando Knip para recortar los paquetes que no deberían estar.
Artículos relacionados
Studio
Empieza un proyecto.
Un partner único para el producto digital que necesitas construir. Producción más rápida, tecnología moderna, costes reducidos. Un equipo, una factura.