Node v12 → v22 en producción sin romper todo: estrategia y lecciones
El plan que usamos para migrar 15+ servicios de Node 12 a Node 22 en Kubernetes sin downtime.
Contexto
15 microservicios en Node 12 (sí, EOL hace años). Kubernetes en GKE. La deuda técnica ya no era negociable — dependencias sin parches de seguridad, features de lenguaje atrasados 5 años.
La estrategia: saltos incrementales
No hicimos 12 → 22 directo. Fuimos 12 → 18 → 22, porque:
- Node 18 fue el primer LTS con cambios breaking manejables desde v12
- Node 22 tiene
require(esm)experimental, que necesitábamos para una migración gradual de CJS → ESM
# Multi-stage para testear en ambas versiones durante la transición
FROM node:18-alpine AS build-18
COPY . .
RUN npm ci && npm test
FROM node:22-alpine AS build-22
COPY . .
RUN npm ci && npm test
Los breaking changes que más dolieron
1. dns.lookup cambió de orden
// Node 12: resolvía IPv4 primero por defecto
// Node 18+: respeta el orden del OS
// Fix: ser explícito
dns.lookup(hostname, { family: 4 }, callback);
2. OpenSSL 3.0
# Error que aparecía en todas partes
Error: error:0308010C:digital envelope routines::unsupported
No usamos --openssl-legacy-provider. Actualizamos las dependencias que usaban algoritmos legacy.
3. fetch global
Pudimos eliminar node-fetch de 12 servicios. Menos dependencias, menos surface area.
Rollout
Usamos canary deployments en Kubernetes — 10% del tráfico al pod con Node 22, monitoreando latencia y error rate en Datadog durante 48h antes de ir full.
# deployment canary
spec:
replicas: 1
template:
spec:
containers:
- name: api
image: registry/service:node22-canary
Resultado
- Zero downtime en la migración
- -23% memory footprint promedio (V8 mejoró bastante)
- Todas las deps actualizadas a versiones con soporte