🔒 Lockfiles y Reproducibilidad: El Sello de la Consistencia

“Dos ninjas que entrenan la misma técnica deben obtener el mismo resultado. Sin variación, sin sorpresas.” - Código de la Reproducibilidad

🎯 El Problema de la No Reproducibilidad

Escenario Sin Lockfile (Antes de npm 5)

Tu package.json:

{
  "dependencies": {
    "express": "^4.18.0"
  }
}

Tú instalas hoy:

npm install
# Descarga express 4.18.2

Tu compañero instala mañana (después de que salió 4.19.0):

npm install
# Descarga express 4.19.0  ← Versión diferente

Resultado:

  • Tu app funciona en tu máquina
  • Falla en producción
  • Nadie sabe por qué
  • Pasan 3 horas debuggeando
  • Descubren que la versión es diferente

📜 ¿Qué es un Lockfile?

Un lockfile registra las versiones exactas de TODAS las dependencias (incluyendo sub-dependencias) instaladas en tu proyecto.

Propósito:

  1. Garantizar reproducibilidad: Misma instalación en todos lados
  2. Acelerar instalación: No necesita resolver versiones
  3. Auditar cambios: Git muestra qué paquetes cambiaron
  4. Seguridad: Verifica integridad con checksums

🔍 Tipos de Lockfiles

1. package-lock.json (npm)

Formato: JSON

Ubicación: Raíz del proyecto

Cuándo se crea: Primera vez que ejecutas npm install

Ejemplo:

{
  "name": "mi-proyecto",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "dependencies": {
        "express": "^4.18.2"
      }
    },
    "node_modules/express": {
      "version": "4.18.2",
      "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
      "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
      "dependencies": {
        "accepts": "~1.3.8",
        "body-parser": "1.20.1",
        "content-disposition": "0.5.4"
      }
    },
    "node_modules/accepts": {
      "version": "1.3.8",
      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="
    }
  }
}

Campos clave:

  • version: Versión exacta instalada
  • resolved: URL exacta de descarga
  • integrity: Checksum SHA-512 (verificar archivo no fue alterado)

2. pnpm-lock.yaml (pnpm)

Formato: YAML

Ventaja: Más legible que JSON, menos conflictos en Git

Ejemplo:

lockfileVersion: '6.0'

dependencies:
  express:
    specifier: ^4.18.2
    version: 4.18.2

packages:

  /express@4.18.2:
    resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1...}
    dependencies:
      accepts: 1.3.8
      body-parser: 1.20.1
    dev: false

  /accepts@1.3.8:
    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC...}
    dev: false

3. yarn.lock (yarn)

Formato: Custom (similar a YAML pero propio)

Ejemplo:

express@^4.18.2:
  version "4.18.2"
  resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
  integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
  dependencies:
    accepts "~1.3.8"
    body-parser "1.20.1"
    content-disposition "0.5.4"

accepts@~1.3.8:
  version "1.3.8"
  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
  integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==

4. composer.lock (PHP/Composer)

Formato: JSON

Ejemplo:

{
    "_readme": [
        "This file locks the dependencies of your project to a known state",
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies"
    ],
    "content-hash": "d751713988987e9331980363e24189ce",
    "packages": [
        {
            "name": "guzzlehttp/guzzle",
            "version": "7.8.1",
            "source": {
                "type": "git",
                "url": "https://github.com/guzzle/guzzle.git",
                "reference": "41042bc7ab68f23..."
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc...",
                "reference": "41042bc...",
                "shasum": ""
            }
        }
    ]
}

🔍 Comparación de Lockfiles

GestorLockfileFormatoTamañoLegibilidadConflictos Git
npmpackage-lock.jsonJSONGrandeBajaFrecuentes
pnpmpnpm-lock.yamlYAMLMedioAltaRaros
yarnyarn.lockCustomMedioMediaMedios
Composercomposer.lockJSONMedioMediaMedios

🎯 Flujos de Trabajo con Lockfiles

Instalación Inicial

# Usuario A crea proyecto
npm init -y
npm install express

# Resultado:
# - node_modules/ (carpeta)
# - package.json (actualizado)
# - package-lock.json (CREADO)

Clonar Proyecto

# Usuario B clona repo
git clone https://github.com/user/proyecto.git
cd proyecto

# Instalar EXACTAMENTE las mismas versiones
npm install
# npm lee package-lock.json y usa versiones fijadas

Actualizar Dependencias

# Actualizar paquetes (respetando rangos de package.json)
npm update

# package-lock.json se actualiza automáticamente
git add package-lock.json
git commit -m "chore: actualizar dependencias"

Añadir Nueva Dependencia

npm install lodash

# package.json: añade "lodash": "^4.17.21"
# package-lock.json: añade entrada con versión exacta

⚠️ Errores Comunes y Soluciones

Error #1: Ignorar Lockfile en Git

# ❌ NUNCA HAGAS ESTO
package-lock.json
pnpm-lock.yaml
yarn.lock
composer.lock

Por qué es malo:

  • Pierdes reproducibilidad
  • Cada instalación puede ser diferente
  • Dificulta debugging en producción

Solución:

# ✅ CORRECTO
node_modules/
vendor/

# NO ignores lockfiles

Error #2: Editar Lockfile Manualmente

// ❌ Nunca edites package-lock.json a mano
{
  "express": {
    "version": "4.18.2"  // Cambiaste esto manualmente
  }
}

Consecuencia:

  • Checksum no coincide → error de integridad
  • Instalaciones futuras fallan

Solución:

# Elimina lockfile y regenera
rm package-lock.json
npm install

Error #3: Mezclar Gestores en un Proyecto

# Desarrollador A usa npm
npm install express

# Desarrollador B usa yarn
yarn add lodash

# Resultado:
# - package-lock.json (de npm)
# - yarn.lock (de yarn)
# - CONFLICTO: dos fuentes de verdad

Solución:

Elige un gestor y documéntalo:

# README.md

## Instalación

**Importante:** Este proyecto usa npm. NO uses yarn o pnpm.

\`\`\`bash
npm install
\`\`\`

Error #4: Conflictos de Merge en Git

# Desarrollador A actualiza express
npm update express

# Desarrollador B actualiza react
npm update react

# Git merge → CONFLICTO en package-lock.json

Solución:

# 1. Aceptar ambos cambios en package.json

# 2. Eliminar package-lock.json conflictivo
rm package-lock.json

# 3. Regenerar
npm install

# 4. Commitear lockfile nuevo
git add package-lock.json
git commit -m "chore: resolver conflictos de dependencias"

💡 CI/CD y Lockfiles

npm ci vs npm install

# npm install
# - Lee package.json
# - Actualiza package-lock.json si hay diferencias
# - Instala paquetes

# npm ci (Continuous Integration)
# - Lee package-lock.json SOLAMENTE
# - FALLA si package.json y lockfile no coinciden
# - Borra node_modules/ antes de instalar
# - Instalación más rápida (no resuelve versiones)

Uso en CI/CD:

# GitHub Actions
- name: Install dependencies
  run: npm ci  # ✅ Más rápido y seguro

Equivalentes en Otros Gestores

# npm
npm ci

# yarn
yarn install --frozen-lockfile

# pnpm
pnpm install --frozen-lockfile

# Composer
composer install --no-dev

🎯 Buenas Prácticas

1. Siempre Versiona Lockfiles

git add package-lock.json
git commit -m "chore: actualizar lockfile"

2. Usa npm ci en CI/CD

# ✅ Correcto
- run: npm ci

# ❌ Incorrecto (puede instalar versiones diferentes)
- run: npm install

3. Regenera Lockfile si Falla Instalación

# Si npm install falla misteriosamente
rm package-lock.json
npm install

4. Revisa Cambios en Lockfile

# Ver qué paquetes cambiaron
git diff package-lock.json

5. Documenta Qué Gestor Usar

// package.json
{
  "engines": {
    "npm": ">=9.0.0"
  }
}

🎯 Reto Ninja del Tema

Misión: Entender lockfiles en acción

  1. Crea dos proyectos idénticos:

    mkdir proyecto-a proyecto-b
    cd proyecto-a
    npm init -y
    npm install express
    
    cd ../proyecto-b
    npm init -y
    npm install express
  2. Compara lockfiles:

    diff proyecto-a/package-lock.json proyecto-b/package-lock.json
    # Deberían ser IDÉNTICOS
  3. Simula actualización:

    cd proyecto-a
    npm update express
    git diff package-lock.json  # Ver cambios
  4. Rompe el lockfile:

    # Borra versión de express manualmente en package-lock.json
    npm install  # Verás error de integridad
  5. Repara:

    rm package-lock.json
    npm install

Criterios de éxito:

  • ✅ Entiendes que lockfiles garantizan reproducibilidad
  • ✅ Sabes regenerar lockfile si se corrompe
  • ✅ Nunca editarás lockfiles manualmente
  • ✅ Versionarás lockfiles en Git

📚 Recursos Adicionales


Siguiente Pergamino: Módulo 5: Introducción a PHP

¿Entendiste lockfiles? Comparte tu experiencia en Discord con #LockfilesNinja! 🔒🥷