En la lección anterior instalaste dotenv y npm escribió ^17.2.1 en el manifiesto, no 17.2.1. Ese acento circunflejo es una decisión con consecuencias enormes: significa que tu proyecto no está pidiendo una versión, está pidiendo un rango, y que la versión concreta que acabe instalada depende de qué haya publicado el autor de dotenv el día que alguien ejecute npm install.
Eso es una bomba de relojería o una comodidad extraordinaria, según lo entiendas. Sin package-lock.json, dos personas del mismo equipo pueden instalar el mismo package.json en días distintos y acabar con árboles de dependencias diferentes. Con él, la instalación es reproducible hasta el último byte.
Esta lección tiene dos mitades que se explican mutuamente: el sistema de versiones que promete compatibilidad y el fichero que hace que esa promesa no haga falta.
Contenido
- SemVer:
MAYOR.MENOR.PARCHEy el contrato implícito - Versiones de preproducción y su orden de precedencia
- La tabla de rangos, con la fila que casi nadie conoce
- Qué instala cada rango hoy y dentro de seis meses
package-lock.json: qué contiene exactamentenpm cifrente anpm install- Conflictos del lock en git
npm dedupey--save-exact- La actualización menor que rompe
- De
0.1.0a1.0.0: versionar Escena Viva
- SemVer:
MAYOR.MENOR.PARCHE y el contrato implícito
MAYOR.MENOR.PARCHE y el contrato implícitoEl versionado semántico (semver.org) es una convención que convierte tres números en una promesa verificable:
17 . 2 . 1
│ │ │
│ │ └── PARCHE: correcciones compatibles
│ └────────── MENOR: funcionalidad nueva, compatible
└────────────────── MAYOR: cambios que rompen compatibilidadLas reglas de cuándo sube cada número:
| Tipo de cambio | Sube | Ejemplo |
|---|---|---|
| Corregir un fallo sin cambiar la API | PARCHE | 17.2.1 → 17.2.2 |
| Añadir una función u opción nueva | MENOR | 17.2.1 → 17.3.0 |
| Marcar algo como obsoleto (sin quitarlo) | MENOR | 17.2.1 → 17.3.0 |
| Quitar o renombrar algo público | MAYOR | 17.2.1 → 18.0.0 |
| Cambiar el comportamiento de una función existente | MAYOR | 17.2.1 → 18.0.0 |
| Subir la versión mínima de Node exigida | MAYOR | 17.2.1 → 18.0.0 |
| Cambiar comentarios, README o pruebas internas | Nada | No se publica |
Al subir un número, los de su derecha vuelven a cero: de 17.2.1 a menor es 17.3.0, y a mayor es 18.0.0.
El contrato implícito es la parte que se olvida. Publicar 17.3.0 en vez de 18.0.0 no es una preferencia estética: es afirmar ante miles de proyectos que pueden actualizar sin revisar su código. Y como el 90 % de esos proyectos tiene escrito ^17.2.1, esa versión menor entrará en sus instalaciones automáticamente. Si mentiste, sus compilaciones se rompen mañana sin que ellos hayan cambiado una línea.
Esa es la razón de que semver sea, antes que una regla técnica, una cuestión de responsabilidad. Y también la razón de que no puedas fiarte del todo de él: es una convención, no algo que el registro verifique. Lo que sí verifica es el fichero de bloqueo, y por eso las dos mitades de la lección van juntas.
- Versiones de preproducción y su orden de precedencia
Antes de una versión estable se publican versiones de prueba, con un guion:
Las reglas de precedencia, definidas por la especificación: una versión con etiqueta de preproducción es siempre menor que la misma sin ella (1.0.0-rc.1 < 1.0.0, porque es un ensayo de esa versión, no una posterior); los identificadores se comparan de izquierda a derecha; los numéricos como números y los alfanuméricos alfabéticamente; un número siempre es menor que un texto (1.0.0-1 < 1.0.0-alpha); y a igualdad de los anteriores, gana quien tenga más identificadores (1.0.0-alpha < 1.0.0-alpha.1).
Una serie completa ordenada:
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta
< 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0Fíjate en beta.2 < beta.11: como el identificador es numérico se compara como número, no como texto. Si se comparara como texto, 11 iría antes que 2.
Lo más importante en el día a día: las versiones de preproducción no entran nunca en un rango normal. Si tu manifiesto dice ^1.0.0 y se publica 2.0.0-beta.1, npm no la instalará. Para recibirlas hay que pedirlas explícitamente (npm install paquete@beta) o escribir un rango que las incluya. Es una protección deliberada, y es correcta.
- La tabla de rangos, con la fila que casi nadie conoce
Esta es la tabla que hay que saberse de memoria. Supongamos que la última publicada es 1.9.2 y que existen también 2.0.0 y 1.2.9.
Rango en package.json |
Nombre | Permite | Instala hoy |
|---|---|---|---|
1.2.3 |
Exacto | Solo 1.2.3 |
1.2.3 |
~1.2.3 |
Tilde | Parches: >=1.2.3 <1.3.0 |
1.2.9 |
^1.2.3 |
Circunflejo | Menores y parches: >=1.2.3 <2.0.0 |
1.9.2 |
1.2.x / 1.2 |
Comodín parcial | Igual que ~1.2.0 |
1.2.9 |
1.x / 1 |
Comodín parcial | Igual que ^1.0.0 |
1.9.2 |
>=1.2.3 <2 |
Rango explícito | Lo que dice | 1.9.2 |
1.2.3 - 1.5.0 |
Guion | Ambos extremos incluidos | 1.5.0 |
* o "" |
Cualquiera | Cualquier versión | 2.0.0 |
latest |
Etiqueta | La marcada como latest |
2.0.0 |
La forma corta de recordar las dos importantes: ~ deja subir el último número; ^ deja subir el último que no sea cero por la izquierda.
La fila que casi nadie conoce: ^0.x.y
Aquí está el detalle que produce sorpresas desagradables. El circunflejo se define como "no cambies el primer número distinto de cero", y eso da un comportamiento distinto cuando la mayor es 0:
| Rango | Equivale a | Comentario |
|---|---|---|
^1.2.3 |
>=1.2.3 <2.0.0 |
El habitual |
^0.2.3 |
>=0.2.3 <0.3.0 |
Solo parches: 0.2 actúa como si fuera la mayor |
^0.0.3 |
>=0.0.3 <0.0.4 |
Solo esa versión: equivale a fijarla |
La lógica es sensata: en 0.x la biblioteca declara que su API todavía se mueve, así que semver trata cada versión menor como potencialmente incompatible. La consecuencia práctica sí sorprende: un npm update no mueve nada en un paquete 0.x salvo parches. Si esperabas pasar de 0.2.9 a 0.4.0 automáticamente, no ocurrirá; hay que cambiar el rango a mano.
Y el corolario para cuando publiques tú: mientras estés en 0.x no prometes nada, y quien te use lo sabe. Es exactamente por eso que Escena Viva empezó en 0.1.0 y no en 1.0.0.
- Qué instala cada rango hoy y dentro de seis meses
El experimento mental que aclara el problema. Hoy, con [email protected] como última publicada:
| Rango | Instala hoy |
|---|---|
17.2.1 |
17.2.1 |
~17.2.1 |
17.2.1 |
^17.2.1 |
17.2.1 |
Los tres coinciden. Ahora avanza seis meses: se han publicado 17.2.5, 17.6.0 y 18.0.0. Alguien clona el repositorio, ejecuta npm install sin haber tocado el package.json y obtiene:
| Rango | Instala en seis meses | ¿Ha cambiado tu código? |
|---|---|---|
17.2.1 |
17.2.1 |
No |
~17.2.1 |
17.2.5 |
No |
^17.2.1 |
17.6.0 |
No |
* |
18.0.0 |
No |
Tres personas con el mismo repositorio y tres árboles distintos. Ese es el problema exacto que resuelve el fichero de bloqueo.
Compara la recomendación de cada rango según el papel del proyecto:
| Situación | Rango recomendado | Motivo |
|---|---|---|
| Aplicación con lock (Escena Viva) | ^ |
El lock ya fija la versión real; el rango solo marca la política de actualización |
| Biblioteca publicada | ^ amplio |
Rangos estrechos causan duplicados en los proyectos que te usan |
| Dependencia con historial de romper | ~ o exacto |
Menos margen para sorpresas |
| Cualquier proyecto | Nunca * |
Renuncias a toda garantía a cambio de nada |
package-lock.json: qué contiene exactamente
package-lock.json: qué contiene exactamenteSe genera solo, en cuanto haya alguna dependencia. Este es un fragmento real, recortado:
{
"name": "escena-viva",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "escena-viva",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"dotenv": "^17.2.1"
},
"devDependencies": {
"prettier": "^3.6.2"
},
"engines": {
"node": ">=24.5.0 <25"
}
},
"node_modules/dotenv": {
"version": "17.2.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
"integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78hjB1PYYow==",
"engines": { "node": ">=12" },
"funding": { "url": "https://dotenvx.com" }
},
"node_modules/prettier": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfQqFsuHLoDrIhcVYWyO0nAvqEhIkKMYQIsy0mRnNlqUXWkuVjK4X8oEeWmnCLYUEQA==",
"dev": true,
"bin": { "prettier": "bin/prettier.cjs" },
"engines": { "node": ">=14" }
}
}
}Campo por campo:
| Campo | Qué es |
|---|---|
lockfileVersion: 3 |
Formato del fichero. La 3 es la de npm 7 en adelante |
packages[""] |
El propio proyecto: copia de sus rangos declarados |
version |
La versión exacta resuelta. Aquí no hay rangos |
resolved |
URL exacta del tarball descargado |
integrity |
Hash SHA-512 del contenido, en formato SRI |
dev: true |
Marca que solo hace falta en desarrollo (permite --omit=dev) |
bin |
Ejecutables que se enlazarán en node_modules/.bin |
engines |
Requisitos de Node del paquete, copiados para poder validarlos |
El campo integrity es el que hace que esto sea seguridad y no solo comodidad. En cada instalación npm descarga el tarball, calcula su SHA-512 y lo compara con el registrado. Si no coincide —porque alguien manipuló el registro, porque un espejo sirvió otra cosa, porque una caché se corrompió— la instalación falla. Es integridad criptográfica de todo tu árbol de dependencias, gratis.
Y de ahí la regla que no se negocia:
package-lock.jsonSÍ se sube al control de versiones. Siempre.
Las razones: reproducibilidad (tu portátil, el de tu compañera, la CI y el servidor instalan lo mismo, hoy y dentro de un año); seguridad, porque los hashes viajan con el repositorio; depuración, ya que git log package-lock.json te dice qué versión cambió y cuándo, que suele ser la respuesta a "esto funcionaba la semana pasada"; y auditoría, porque npm audit analiza el árbol exacto del lock y no una aproximación.
La única excepción real es una biblioteca publicada: su lock no afecta a quien la instala, porque cada proyecto resuelve su propio árbol. Aun así, muchas bibliotecas lo suben para que sus propias pruebas sean reproducibles. Escena Viva es una aplicación: lo sube sin discusión.
Y su reverso: el lock nunca se edita a mano. Es un fichero generado. Se cambia ejecutando comandos de npm.
npm ci frente a npm install
npm ci frente a npm installCon el lock ya sobre la mesa, la comparación clave del módulo:
npm install |
npm ci |
|
|---|---|---|
Lee package.json |
Sí | Sí |
Lee package-lock.json |
Sí, si existe | Obligatorio: sin lock, falla |
| Modifica el lock | Sí, si hace falta | Nunca |
| Si el lock no concuerda con el manifiesto | Lo actualiza en silencio | Falla con error |
node_modules previo |
Lo actualiza de forma incremental | Lo borra entero y reinstala |
| Velocidad | Más lenta (resuelve versiones) | Más rápida (solo instala lo escrito) |
Acepta npm ci <paquete> |
Sí, npm install <paquete> |
No existe |
| Uso previsto | Desarrollo, añadir dependencias | CI, Docker, producción |
La fila decisiva es la cuarta. Si alguien edita el package.json a mano —sube un rango, añade una dependencia— y no ejecuta npm install, el lock queda desincronizado. En ese estado, npm install lo arregla en silencio; npm ci se planta:
npm error `npm ci` can only install packages when your package.json and npm error package-lock.json are in sync. Please update your lock file with npm error `npm install` before continuing.
Ese error es una buena noticia: la CI ha detectado que el repositorio está incoherente antes de desplegar nada. Por eso la regla operativa del módulo, que reutilizaremos en el Módulo 11:
- En tu máquina:
npm install,npm install <paquete>,npm update. Modifican el lock, y ese cambio se confirma en git como parte del trabajo. - En CI, Docker y producción:
npm ci(con--omit=deven producción). Nuncanpm install.
- Conflictos del lock en git
Es inevitable: dos ramas añaden dependencias distintas, se fusionan, y git presenta un conflicto en un fichero generado de tres mil líneas.
Lo que no se hace: abrir el fichero y elegir a mano entre marcadores <<<<<<<. El lock es un grafo coherente; resolverlo por trozos produce un árbol inconsistente que "casi" funciona.
Lo que se hace: descartar el fichero y regenerarlo desde los manifiestos, que sí son legibles por humanos.
# 1. Resuelve primero el conflicto de package.json, que es pequeño y legible.
git checkout --ours package-lock.json # o --theirs: da igual, se va a regenerar
npm install # regenera el lock coherente con el manifiesto fusionado
npm ci && npm test # verifica que el resultado instala y funciona
git add package.json package-lock.json
git commitAlternativa igual de válida: borrar el lock y ejecutar npm install. Es más agresivo —puede subir versiones dentro de los rangos, ya que se resuelve todo de nuevo— así que la primera opción es preferible cuando quieres tocar lo mínimo.
Consejo de higiene: una dependencia nueva merece su propia confirmación, con package.json y package-lock.json juntos y nada más. Así el conflicto, si llega, es diminuto, y la revisión de código puede mirar de verdad qué entró (05-06).
npm dedupe y --save-exact
npm dedupe y --save-exactnpm dedupe reorganiza el árbol para compartir el máximo de copias. Tras varias instalaciones sucesivas es fácil acabar con duplicados que ya no hacen falta:
node_modules/
├── paquete-a/
│ └── node_modules/utilidad/ <- 1.4.0
└── paquete-b/
└── node_modules/utilidad/ <- 1.5.0Si ambos rangos admiten 1.5.0, npm dedupe sube una sola copia a la raíz y borra las anidadas. Menos disco, menos superficie de auditoría, y adiós al problema de instanceof entre copias distintas de la misma clase.
Modifica node_modules y el lock, así que se ejecuta en desarrollo y se confirma el resultado. Nunca en producción.
--save-exact (alias -E) guarda la versión sin ^:
O como política del proyecto, en el .npmrc de 05-01:
¿Compensa? La respuesta honesta es casi nunca en una aplicación con lock, porque el lock ya fija la versión real y el rango solo expresa qué política de actualización aceptas. Con ^ y lock, npm ci es igual de determinista.
Fijar versiones exactas sí compensa en tres casos concretos:
| Caso | Motivo |
|---|---|
| Dependencia con historial de romper en versiones menores | Menos margen para sorpresas |
| Herramientas que afectan a la salida generada (compiladores, formateadores) | Que la salida no cambie sin avisar |
| Entorno regulado que exige justificar cada cambio de versión | Toda subida es explícita y revisable |
El coste es real: con versiones exactas, npm update deja de servir y actualizar exige tocar el manifiesto paquete a paquete. En un proyecto grande eso se delega a Dependabot o Renovate (05-06).
- La actualización menor que rompe
El escenario que justifica todo lo anterior. Un martes, la CI de Escena Viva falla. Nadie ha tocado el código desde el viernes.
Causa: [email protected] se publicó el lunes. Tu manifiesto dice ^17.2.1, la CI no usaba lock y npm install trajo la versión nueva. Un cambio de comportamiento que el autor consideró menor —cómo interpreta las comillas en los valores del .env, por ejemplo— rompe una de tus pruebas.
Lo que hay que extraer de esa historia:
^ no es una garantía, es una expectativa. Confía en que un tercero clasifique bien su propio cambio. La mayoría lo hace; equivocarse es fácil, porque nadie conoce todos los usos de su biblioteca. Un cambio que el autor ve como corrección puede ser el comportamiento del que tú dependías.
El lock convierte esa expectativa en un hecho. Con package-lock.json en el repositorio y npm ci en la CI, el martes se habría instalado 17.2.1, igual que el viernes. La versión nueva entraría el día que alguien ejecute npm update deliberadamente, vea el cambio en el diff del lock y pase las pruebas antes de fusionar.
La diferencia no es que el fallo desaparezca: es quién controla cuándo ocurre. Sin lock, te sorprende un martes por la mañana en producción. Con lock, ocurre en una rama, con un responsable y con pruebas delante.
Y por eso el orden correcto de actualizar es siempre el mismo:
npm outdated # que hay disponible y de que tipo
npm update # sube dentro de los rangos: seguro
npm test # verifica (Modulo 9)
git add package-lock.json && git commit -m "Actualiza dependencias menores"Para saltar una versión mayor, el proceso es distinto y no automatizable: leer las notas de la versión, cambiar el rango a mano, adaptar el código y probar. Rama aparte, siempre.
- De
0.1.0 a 1.0.0: versionar Escena Viva
0.1.0 a 1.0.0: versionar Escena VivaEscena Viva nació en 0.1.0 porque su API se movía. Terminado el Módulo 4, tiene una API HTTP pública y estable —GET /api/eventos, POST /pedidos—, un dominio asentado y un cliente (el front-end de publico/) que depende de ella. Es el momento del salto.
npm version hace tres cosas, no una: cambia version en package.json y en package-lock.json, crea una confirmación de git con ese cambio (con 1.0.0 como mensaje por defecto) y crea una etiqueta de git anotada, v1.0.0, que es lo único que imprime por pantalla.
También acepta los saltos por nombre, que es como se usa a diario:
npm version patch # 1.0.0 -> 1.0.1
npm version minor # 1.0.1 -> 1.1.0
npm version major # 1.1.0 -> 2.0.0
npm version 1.1.0-beta.1 --preid=betaSi no quieres la confirmación ni la etiqueta —porque tu flujo de publicación las genera de otro modo— existe --no-git-tag-version.
Qué implica de verdad el salto a 1.0.0 en Escena Viva:
Antes (0.x) |
Después (1.x) |
|---|---|
| Cualquier versión menor podía romper | Solo un salto a 2.0.0 puede romper |
Los rangos ^0.1.0 solo admitían parches |
^1.0.0 admite toda la serie 1.x |
| Cambiar la API era rutina | Cambiar la API exige plan de migración |
En concreto, a partir de ahora: quitar un campo de la respuesta de GET /api/eventos es mayor; añadir un campo nuevo es menor; cambiar el significado de error.codigo en la tabla de errores del Módulo 4 es mayor; corregir un cálculo de aforo mal hecho es parche.
Ese compromiso es el mismo que has estado exigiendo a dotenv durante toda la lección. Aquí cambias de lado de la mesa, y por eso el módulo continúa hacia la publicación.
Errores Comunes y Consejos
- Añadir
package-lock.jsonal.gitignore. Es el error más caro del módulo: destruye la reproducibilidad. Lo que se ignora esnode_modules/, nunca el lock. - Resolver conflictos del lock a mano. Produce árboles incoherentes que fallan de formas raras. Regenéralo con
npm install. - Usar
npm installen la CI. Puede modificar el lock durante la compilación y desplegar algo distinto de lo que probaste.npm ci, siempre. - Esperar que
npm updatesuba una versión mayor. No lo hace nunca, por diseño. Ni tampoco menores en paquetes0.x, por la regla de^0.x.y. - Publicar
1.0.0sin querer prometer estabilidad. Si tu API todavía se mueve, quédate en0.x: es información honesta para quien te use. - Poner
*olatestcomo rango. Cualquier versión mayor entra sin avisar. No hay ningún caso en que compense. - Consejo: revisa el diff del lock. En una revisión de código, un lock que cambia sin que cambie
package.jsonmerece una pregunta. - Consejo:
npm view <paquete> versions --jsonlista todas las versiones publicadas. Útil para ver el ritmo de lanzamientos antes de adoptar una dependencia.
Ejercicios
Ejercicio 1. Resolver rangos a mano.
Un paquete tiene publicadas estas versiones: 0.9.0, 1.0.0, 1.2.0, 1.2.5, 1.9.0, 2.0.0-beta.1, 2.0.0, 2.1.0. Di qué instalaría cada uno de estos rangos: ^1.2.0, ~1.2.0, 1.2.x, >=1.0.0 <2, ^2.0.0, *, 1.2.0 - 1.9.0. Después repite el ejercicio suponiendo que las versiones publicadas son 0.1.0, 0.2.0, 0.2.5, 0.3.0 y el rango es ^0.2.0.
Ejercicio 2. Ver el lock en acción.
En Escena Viva, ejecuta npm ls dotenv y anota la versión. Abre package-lock.json y localiza su version, resolved e integrity. Ahora borra node_modules, ejecuta npm ci y comprueba que la versión es idéntica. Después edita el package.json cambiando el rango a ^17.0.0 sin ejecutar npm install y lanza npm ci. Explica qué pasa y por qué es el comportamiento deseable.
Ejercicio 3. Versionar Escena Viva.
Ejecuta npm version 1.0.0 con el árbol de git limpio. Comprueba con git log -1 y git tag qué ha creado exactamente. Después decide qué tipo de salto (parche, menor o mayor) corresponde a cada cambio: (a) añadir el campo salaAccesible a la respuesta de GET /api/eventos; (b) corregir el cálculo de plazas libres, que restaba mal; (c) renombrar precioCentimos a importeCentimos en la respuesta de la API; (d) cambiar UMBRAL_AFORO_BAJO de 20 a 15; (e) exigir Node 26 en engines.
Soluciones
Solución 1. Primera parte:
| Rango | Instala | Motivo |
|---|---|---|
^1.2.0 |
1.9.0 |
>=1.2.0 <2.0.0; la mayor de la serie 1 |
~1.2.0 |
1.2.5 |
>=1.2.0 <1.3.0; solo parches |
1.2.x |
1.2.5 |
Equivalente a ~1.2.0 |
>=1.0.0 <2 |
1.9.0 |
Excluye explícitamente la serie 2 |
^2.0.0 |
2.1.0 |
No 2.0.0-beta.1: las preproducción no entran en rangos normales |
* |
2.1.0 |
La más alta estable |
1.2.0 - 1.9.0 |
1.9.0 |
Guion incluye ambos extremos |
Segunda parte: ^0.2.0 instala 0.2.5, no 0.3.0. Es la regla de ^0.x.y: con la mayor a cero, el circunflejo protege también el número menor, porque 0.3.0 se considera potencialmente incompatible. Esta es la respuesta que más gente falla.
Solución 2. Tras npm ci, npm ls dotenv muestra exactamente la misma versión: el lock manda, no el rango.
Al cambiar el rango a ^17.0.0 sin instalar, npm ci falla:
npm error `npm ci` can only install packages when your package.json and npm error package-lock.json are in sync. npm error Invalid: lock file's [email protected] does not satisfy dotenv@^17.0.0
Es deseable porque la desincronización es un error real del repositorio, no un detalle. Alguien tocó el manifiesto sin regenerar el lock, así que nadie sabe qué árbol se pretendía desplegar. npm install lo taparía en silencio y desplegaría una resolución que nadie ha revisado; npm ci detiene el despliegue y obliga a arreglarlo en una rama. Se arregla ejecutando npm install y confirmando el lock resultante.
Solución 3. npm version 1.0.0 produce:
$ git log -1 --oneline a1b2c3d 1.0.0 $ git tag v1.0.0 $ git show --stat HEAD package.json | 2 +- package-lock.json | 4 ++--
Clasificación de los cambios:
| Cambio | Salto | Razón |
|---|---|---|
(a) Añadir salaAccesible |
Menor → 1.1.0 |
Añade información; un cliente que la ignore sigue funcionando |
| (b) Corregir plazas libres | Parche → 1.0.1 |
Corrige un fallo sin cambiar la forma de la respuesta |
(c) Renombrar a importeCentimos |
Mayor → 2.0.0 |
Todo cliente que leyera precioCentimos se rompe |
(d) UMBRAL_AFORO_BAJO de 20 a 15 |
Menor → 1.1.0 |
Cambia el comportamiento observable sin romper contratos; si esa constante estuviera documentada como parte de la API pública, sería mayor |
| (e) Exigir Node 26 | Mayor → 2.0.0 |
Deja fuera entornos que antes funcionaban |
El caso (d) es el interesante: la respuesta depende de si esa constante forma parte del contrato público. Esa ambigüedad es exactamente la parte difícil de semver, y la razón de que un CHANGELOG claro valga tanto como los números.
Conclusión
Ya sabes leer el lenguaje con el que el ecosistema entero se comunica. SemVer convierte tres números en una promesa: PARCHE corrige, MENOR añade sin romper, MAYOR rompe. Las versiones de preproducción son siempre menores que su estable y no entran en los rangos normales. Y de la tabla de rangos te llevas dos formas: ~ deja subir el último número, ^ el último que no sea cero por la izquierda —de ahí que ^0.2.3 solo admita parches, la regla que casi nadie conoce y que explica por qué npm update a veces parece no hacer nada.
Pero la lección de fondo es que ^ es una expectativa, no una garantía: depende de que un tercero clasifique bien su propio cambio. Quien convierte esa expectativa en un hecho es package-lock.json, con su árbol exacto, sus versiones resueltas, sus URLs y sobre todo su integrity SHA-512, que hace fallar la instalación si el contenido descargado no es byte a byte el que se registró. Por eso se sube siempre al repositorio, por eso nunca se edita a mano, y por eso sus conflictos en git se resuelven regenerándolo, no eligiendo entre marcadores.
De ahí sale la regla operativa que te acompañará hasta el final del curso: npm install en tu máquina —modifica el lock, y ese cambio se confirma y se revisa— y npm ci en la CI, en Docker y en producción, que exige el lock, lo respeta al pie de la letra, borra node_modules y falla si el manifiesto y el lock no concuerdan. Ese fallo es una buena noticia: detiene un despliegue incoherente antes de que llegue a nadie.
Escena Viva es ya 1.0.0, con su etiqueta v1.0.0 en git creada por npm version, y ese número es un compromiso: a partir de hoy, quitar un campo de la API o cambiar el significado de un error.codigo obliga a subir a 2.0.0.
En la lección siguiente, Scripts de npm y Automatización del Proyecto, llenamos el último hueco del manifiesto. El campo scripts va a convertirse en la interfaz única del proyecto —npm start, npm run dev, npm run informe, npm test— para que nadie tenga que leer el README para adivinar un comando. Y por el camino descubrirás por qué funciona una herramienta que nunca instalaste globalmente: el PATH extendido con node_modules/.bin.
Curso de Node.js: De Principiante a Avanzado
Módulo 1: Introducción a Node.js
- ¿Qué es Node.js?
- Instalación y Configuración del Entorno
- Tu Primer Programa en Node.js
- El REPL de Node.js
- JavaScript Moderno para Node.js
- El Proyecto del Curso: la Plataforma Escena Viva
Módulo 2: Conceptos Básicos
- Arquitectura de Node.js
- El Bucle de Eventos (Event Loop)
- Callbacks y Programación Asíncrona
- Promesas y async/await
- Eventos y EventEmitter
- Módulos CommonJS y require()
- Módulos ES e Interoperabilidad
Módulo 3: Sistema de Archivos y E/S
- Lectura y Escritura de Archivos
- El Módulo fs a Fondo
- Rutas Multiplataforma con el Módulo path
- Trabajando con Streams
- Streams de Transformación y pipeline
- Buffers y Datos Binarios
Módulo 4: HTTP y Servidores Web
- Creando un Servidor HTTP Simple
- Manejo de Solicitudes y Respuestas
- Enrutamiento Manual
- Sirviendo Archivos Estáticos
- Recibiendo Datos: Cuerpos de Petición y JSON
- Consumiendo APIs Externas desde Node.js
Módulo 5: NPM y Gestión de Paquetes
- Introducción a NPM y package.json
- Instalación y Uso de Paquetes
- Versionado Semántico y package-lock
- Scripts de npm y Automatización del Proyecto
- Creación y Publicación de Paquetes
- Seguridad y Mantenimiento de Dependencias
Módulo 6: Framework Express.js
- Introducción a Express.js
- Configuración de una Aplicación Express
- Enrutamiento en Express
- Middleware
- Middleware de Terceros Esenciales
- Validación de Datos de Entrada
- Manejo de Errores
Módulo 7: Bases de Datos y ORMs
- Introducción a las Bases de Datos
- Usando MongoDB con Mongoose
- Operaciones CRUD
- Relaciones, Poblado y Consultas Avanzadas
- Usando Bases de Datos SQL con Sequelize
- Migraciones, Transacciones y Datos de Prueba
Módulo 8: Autenticación y Autorización
- Introducción a la Autenticación
- Registro de Usuarios y Hash de Contraseñas
- Sesiones y Cookies con Passport.js
- Autenticación con JWT
- Control de Acceso Basado en Roles
- Buenas Prácticas de Seguridad en APIs
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias con Mocha y Chai
- Dobles de Prueba con Sinon
- Pruebas de Integración
- Cobertura y Automatización de las Pruebas
- Depuración de Aplicaciones Node.js
Módulo 10: Temas Avanzados
- El Módulo Cluster
- Hilos de Trabajo (Worker Threads)
- Caché y Colas de Trabajo con Redis
- Optimización del Rendimiento
- Construcción de APIs RESTful
- GraphQL con Node.js
Módulo 11: Despliegue y DevOps
- Configuración y Variables de Entorno
- Registro y Monitorización en Producción
- Usando PM2 para la Gestión de Procesos
- Empaquetado con Docker
- Desplegando en Heroku y Otras PaaS
- Integración y Despliegue Continuos
