Lección 2 de 313 min
Gestores de paquetes: npm, pnpm, Yarn y pip
package.json, versionado semántico, lockfiles y cómo instala dependencias cada gestor.
Clonas un proyecto, ejecutas pnpm install y, en unos segundos, aparecen cientos de paquetes que nadie del equipo ha escrito. ¿Cómo sabe el gestor cuáles bajar, en qué versión y dónde ponerlos? ¿Y por qué al compañero que instaló ayer le funciona y a ti no?
Un gestor de paquetes descarga las librerías de las que depende tu proyecto (sus dependencias), resuelve qué versiones encajan entre sí y las deja listas para usar. En esta lección verás las piezas comunes a todos (package.json, el versionado semántico y los lockfiles) y en qué se diferencian npm, pnpm, Yarn y pip, el gestor de Python.
El contrato del proyecto
Todos los gestores de JavaScript leen los mismos dos archivos: package.json, que dice qué quieres, y el lockfile, que apunta exactamente qué se instaló. Entre ambos está el versionado semántico, que decide qué versiones son aceptables.
package.json
La ficha del proyecto: nombre, dependencias y los comandos que usa el equipo.
- Formato:
- JSON
Es un archivo JSON en la raíz del proyecto. Lo más importante que contiene son las dependencias, separadas en dos grupos: dependencies, lo que tu código necesita para funcionar (React, Express), y devDependencies, lo que solo usas mientras desarrollas (TypeScript, Vite, las herramientas de test).
La sección scripts guarda los comandos del proyecto con un nombre corto: pnpm dev, npm run build o npm test. Así nadie tiene que recordar las opciones de cada herramienta, y el CI ejecuta exactamente lo mismo que tú. El campo type: "module" le dice a Node que tus archivos .js usan import, y packageManager indica qué gestor y versión usa el proyecto.
Úsalo cuando
En todo proyecto de JavaScript, aunque no publiques nada. Se crea con npm init (o pnpm init) y se actualiza solo cada vez que instalas algo con npm install paquete.
Ten en cuenta
La diferencia entre los dos grupos importa sobre todo en el servidor y en las librerías: npm install --omit=dev (o pnpm install --prod) instala solo dependencies. En una app de frontend que se empaqueta, todo acaba en el bundle igualmente, pero mantener la separación ayuda a entender qué es de cada cosa.
{
"name": "tienda",
"version": "1.4.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build",
"test": "vitest"
},
"dependencies": {
"react": "^19.1.0",
"zod": "~4.1.5"
},
"devDependencies": {
"typescript": "^5.9.0",
"vite": "^7.1.0",
"vitest": "^3.2.0"
},
"packageManager": "pnpm@10.18.0"
}Versionado semántico y rangos
Tres números que prometen qué tipo de cambio trae cada versión.
- Formato:
- MAYOR.MENOR.PARCHE
- Por defecto:
- ^
El versionado semántico (semver) escribe las versiones como MAYOR.MENOR.PARCHE. Subir el parche (1.2.3 → 1.2.4) significa «corregí errores»; la menor (1.2.3 → 1.3.0), «añadí funciones sin romper nada»; la mayor (1.2.3 → 2.0.0), «hay cambios incompatibles, revisa tu código».
En package.json no sueles fijar una versión, sino un rango. ^1.2.3 (acento circunflejo, el que npm pone por defecto) acepta cualquier 1.x.x desde la 1.2.3; ~1.2.3 (virgulilla) solo parches de la 1.2; 1.2.3 a secas, exactamente esa. Con versiones 0.x el ^ es más estricto, porque antes de la 1.0 se considera que todo puede cambiar.
Úsalo cuando
Cada vez que añades o actualizas una dependencia, y cuando publicas una librería: elegir bien el número de tu versión es una promesa a quien la use.
Ten en cuenta
Semver es una promesa, no una garantía: un autor puede romper algo en una versión menor sin querer. Por eso existen los lockfiles. Y un * o un latest en package.json acepta cualquier versión, incluidas las que rompen tu código.
"^1.2.3" → >=1.2.3 <2.0.0 (por defecto)
"~1.2.3" → >=1.2.3 <1.3.0 (solo parches)
"1.2.3" → =1.2.3 (exacta)
"1.x" → >=1.0.0 <2.0.0
">=2.0.0 <3.0.0" → cualquier 2.x.x
"^0.2.3" → >=0.2.3 <0.3.0 (¡ojo con el 0!)Lockfiles
La foto exacta de lo instalado, para que todos tengan las mismas versiones.
- Se sube a Git:
- Sí
- Se edita a mano:
- No
Un rango como ^1.2.3 puede resolverse en la 1.2.3 hoy y en la 1.9.0 dentro de un mes. El lockfile apunta la versión exacta que se instaló de cada paquete, incluidas las dependencias de tus dependencias, junto con una huella (hash) de su contenido para detectar si alguien lo ha manipulado.
Cada gestor tiene el suyo: package-lock.json (npm), pnpm-lock.yaml (pnpm) y yarn.lock (Yarn). Se sube a Git, y en el CI se instala con un comando que respeta el lockfile al pie de la letra y falla si no coincide con package.json: npm ci, pnpm install --frozen-lockfile o yarn install --immutable.
Úsalo cuando
Siempre en aplicaciones. Es lo que hace que «en mi máquina funciona» también sea cierto en la de tu compañero, en el CI y en producción.
Ten en cuenta
No lo edites a mano ni lo borres para «arreglar» un conflicto de Git: perderías todas las versiones fijadas. Resuelve el conflicto en package.json y vuelve a ejecutar la instalación para que el gestor lo regenere.
# En tu equipo: instala y actualiza el lockfile si hace falta
npm install
# En el CI: borra node_modules e instala exactamente
# lo que dice package-lock.json (falla si no cuadra)
npm ci
# Equivalentes en pnpm y Yarn moderno
pnpm install --frozen-lockfile
yarn install --immutablePruébalo
En el simulador, compara ^1.2.3 con ~1.2.3 y luego con ^0.2.3. Fíjate en qué versión instalaría npm en cada caso: siempre la más alta que cumple el rango.
Interactivo
¿Qué versiones acepta este rango?
Escribe o elige un rango como los de package.json y mira qué versiones publicadas lo cumplen y cuál instalaría npm.
Equivale a >=1.2.3 <2.0.0
Versiones publicadas del paquete
- 0.2.3 no lo cumple
- 0.2.9 no lo cumple
- 0.3.0 no lo cumple
- 1.2.2 no lo cumple
- 1.2.3 cumple el rango
- 1.2.9 cumple el rango
- 1.3.0 cumple el rango
- 1.9.9 cumple el rangoinstalaría
- 2.0.0 no lo cumple
- 2.1.0 no lo cumple
^1.2.3 significa: desde la 1.2.3 sin cambiar el primer número (la versión mayor): acepta correcciones y funciones nuevas, pero no la 2.0.0, que podría traer cambios incompatibles.
npm instalaría la 1.9.9: la más alta de las 4 que cumplen el rango (de 10 publicadas).
Los gestores de JavaScript
npm, pnpm y Yarn descargan los mismos paquetes del mismo registro y entienden el mismo package.json. Se diferencian en cómo guardan los paquetes en tu disco, y eso tiene consecuencias en velocidad, espacio y errores.
npm
El gestor que viene con Node.js y el registro público donde viven los paquetes.
- Viene con:
- Node.js
- Lockfile:
- package-lock.json
npm es dos cosas: el registro público (npmjs.com), donde se publican millones de paquetes, y la herramienta de línea de comandos que se instala junto con Node.js. Con npm install descarga las dependencias, con npm run ejecuta scripts y con npx lanza un paquete sin instalarlo de forma permanente.
Guarda los paquetes en una carpeta node_modules plana: sube al primer nivel también las dependencias de tus dependencias para no repetirlas. Es práctico, pero permite importar por error un paquete que no declaraste en package.json, lo que se llama una dependencia fantasma.
Úsalo cuando
Cuando quieres lo más estándar y compatible, sin instalar nada más: cualquier tutorial, CI o herramienta lo entiende.
Ten en cuenta
Cada proyecto tiene su propia copia de todos los paquetes, así que con muchos proyectos node_modules ocupa mucho disco. Y una dependencia fantasma puede desaparecer en cualquier actualización y romper tu código sin que hayas tocado nada.
npm install zod # añade a dependencies
npm install -D vitest # añade a devDependencies
npm run build # ejecuta el script "build"
npm test # atajo de npm run test
npx serve dist # ejecuta un paquete sin instalarlo
npm outdated # qué dependencias tienen versión nueva
npm audit # vulnerabilidades conocidaspnpm
Un almacén global y enlaces: instala más rápido, ocupa menos y es estricto.
- Lockfile:
- pnpm-lock.yaml
- Lo usa:
- Esta app
pnpm guarda cada versión de cada paquete una sola vez en un almacén global de tu equipo, organizado por el contenido de los archivos (un almacén «direccionable por contenido»). En el node_modules de cada proyecto no copia nada: crea enlaces a ese almacén. Veinte proyectos con React comparten la misma copia en disco.
Además, su node_modules es estricto: tu código solo puede importar lo que declaraste en package.json. Las dependencias fantasma dan error desde el primer día, en lugar de romperse por sorpresa más adelante. Trae los workspaces integrados, que verás en la lección de monorepos. Esta misma app usa pnpm.
Úsalo cuando
En casi cualquier proyecto nuevo, y especialmente en monorepos o si trabajas con muchos proyectos en el mismo equipo.
Ten en cuenta
Alguna herramienta antigua que asume el node_modules plano de npm puede fallar; se arregla declarando la dependencia que falta o con opciones de compatibilidad. En las versiones recientes, pnpm no ejecuta por defecto los scripts de instalación de las dependencias: tienes que autorizar los paquetes que los necesitan.
pnpm add zod # añade a dependencies
pnpm add -D vitest # añade a devDependencies
pnpm dev # ejecuta el script "dev" (sin "run")
pnpm dlx serve dist # como npx
pnpm why react # quién depende de react y por qué
# node_modules/react → enlace al almacén global
# node_modules/.pnpm/ → el resto de paquetes, ordenadosYarn
Nació para corregir a npm; su versión moderna puede funcionar sin node_modules.
- Creado por:
- Desde:
- 2016
- Lockfile:
- yarn.lock
Facebook presentó Yarn en 2016, junto con otras empresas, cuando npm era lento y no generaba un lockfile por defecto. Yarn trajo yarn.lock e instalaciones en paralelo, y npm acabó copiando esas ideas. Esa primera generación, Yarn 1, se conoce como Yarn Classic y hoy solo recibe mantenimiento.
Desde la versión 2, el Yarn moderno (apodado Berry) es casi otra herramienta. Su modo más llamativo es Plug'n'Play (PnP): no crea node_modules, sino un archivo .pnp.cjs que le dice a Node dónde está cada paquete, guardados como archivos comprimidos. Puedes incluso subir esa caché a Git y clonar el proyecto sin instalar nada («zero-installs»).
Úsalo cuando
Si tu proyecto ya usa Yarn, o si te interesan Plug'n'Play y los zero-installs. Para un proyecto nuevo, pnpm suele ser la elección más sencilla.
Ten en cuenta
Comprueba qué Yarn usa un proyecto antes de opinar sobre él: Classic y Berry se comportan de forma muy distinta. Algunas herramientas no se llevan bien con PnP; en ese caso, nodeLinker: node-modules en .yarnrc.yml vuelve al node_modules de siempre.
yarn add zod # añade a dependencies
yarn add -D vitest # añade a devDependencies
yarn dev # ejecuta el script "dev"
yarn dlx serve dist # como npx (Yarn moderno)
# .yarnrc.yml: elige cómo instalar
nodeLinker: pnp # o node-modules, para máxima compatibilidadY en Python: pip
Las mismas ideas aparecen en todos los lenguajes. Si trabajas con Python, para scripts, ciencia de datos o un backend con Django o FastAPI, el equivalente de npm es pip.
pip
El instalador de paquetes de Python, que descarga desde el registro PyPI.
- Lenguaje:
- Python
- Registro:
- PyPI
pip instala paquetes desde PyPI, el registro público de Python. A diferencia de npm, no crea una carpeta por proyecto: instala en el Python que estés usando. Por eso lo habitual es crear antes un entorno virtual (python -m venv .venv), una copia aislada de Python para cada proyecto, y activarlo antes de instalar.
Las dependencias se apuntan tradicionalmente en requirements.txt, una lista de paquetes con sus versiones. El estándar moderno es pyproject.toml, que describe el proyecto completo, como un package.json. pip no ha tenido tradicionalmente un lockfile como los de JavaScript: pip freeze es lo más parecido, y herramientas como Poetry o uv, un gestor muy rápido escrito en Rust, sí generan uno.
Úsalo cuando
En cualquier proyecto de Python. Crea siempre un entorno virtual por proyecto, igual que cada proyecto de JavaScript tiene su node_modules.
Ten en cuenta
Instalar con pip fuera de un entorno virtual mezcla las dependencias de todos tus proyectos, y en muchas distribuciones de Linux recientes da error a propósito para proteger el Python del sistema. Sube requirements.txt a Git, pero nunca la carpeta .venv.
python -m venv .venv # crea el entorno virtual
source .venv/bin/activate # actívalo (en Windows: .venv\Scripts\activate)
pip install requests "pandas>=2.2,<3" # instala con un rango de versiones
pip freeze > requirements.txt # apunta las versiones exactas
# En otra máquina o en el CI:
pip install -r requirements.txtNota
La equivalencia es casi uno a uno: PyPI es el registro de npm, el entorno virtual hace el papel de node_modules, pyproject.toml el de package.json y un requirements.txt con versiones exactas se parece a un lockfile.
Interactivo
npm, pnpm, Yarn y pip
Activa o desactiva opciones para compararlas cara a cara.
| Característica | npm | pnpm | Yarn |
|---|---|---|---|
| Ecosistema | JavaScript | JavaScript | JavaScript |
| Cómo se instala | Viene con Node.js | Aparte | Aparte |
| Dónde guarda los paquetes | node_modules plano en cada proyecto | Almacén global y enlaces; node_modules estricto | node_modules (Classic) o Plug'n'Play (Berry) |
| Lockfile | package-lock.json | pnpm-lock.yaml | yarn.lock |
| Instalación exacta en CI | npm ci | pnpm install --frozen-lockfile | yarn install --immutable |
| Punto fuerte | Es el estándar: lo entiende todo el mundo | Rápido, ahorra disco y evita dependencias fantasma | Plug'n'Play y zero-installs |
Ponlo en práctica
Saber qué es cada cosa es la mitad del trabajo. La otra mitad es reconocer cuándo usarla.
Interactivo
¿Qué usarías?
Lee cada situación y elige la opción que mejor la resuelve. Fallar aquí no cuenta: es para practicar.
Tienes veinte proyectos en tu equipo, todos con React y Vite, y el disco está casi lleno por culpa de tantas carpetas node_modules.
Para recordar
package.jsondice qué necesita el proyecto (y separa lo de desarrollo); el lockfile dice exactamente qué se instaló.- En semver, MAYOR.MENOR.PARCHE:
^acepta cambios menores y parches,~solo parches, y con versiones 0.x todo es más estricto. - Sube el lockfile a Git y usa
npm ciopnpm install --frozen-lockfileen el CI. - pnpm ahorra disco con un almacén global y evita dependencias fantasma con un
node_modulesestricto. - En Python, pip dentro de un entorno virtual cumple el mismo papel que npm con
node_modules.
Comprueba lo aprendido
Cuestionario
Responde las 5 preguntas para completar la lección.
1.Tu
package.jsondice"express": "^4.18.2". ¿Qué versiones podría instalar npm?2.¿Por qué
^0.3.1no acepta la versión 0.4.0?3.¿Qué hace
npm cique no hacenpm install?4.Tu código hace
import debug from "debug"y funciona con npm, pero al pasar a pnpm falla con «Cannot find module».debugno aparece en tupackage.json. ¿Qué ocurre?5.En Python, ¿para qué sirve un entorno virtual creado con
python -m venv .venv?