Skip to content

Receta: Migración de Stack Tecnológico

Esta receta recorre la planificación y ejecución de una migración tecnológica usando SpecForge. Usamos la migración de una API Node.js de Express 4 a Fastify como ejemplo — una migración común que toca enrutamiento, middleware, plugins y manejo de errores.

El mismo workflow aplica a cualquier migración tecnológica: actualizaciones de framework, cambios de motor de base de datos, reemplazos de ORM o actualizaciones de lenguaje.


Antes de Comenzar

Las migraciones son de alto riesgo porque tocan muchos archivos y pueden romper comportamientos de formas sutiles. SpecForge reduce este riesgo al:

  1. Crear una spec de paridad funcional antes de cualquier cambio de código
  2. Generar tests que verifican que el comportamiento coincide antes y después
  3. Proporcionar un plan de migración por fases con rollback en cada paso
  4. Detectar drift a medida que avanza la migración

Paso 1 — Analizar el Codebase Origen

Prompt:

Reverse engineer my codebase at /Users/me/my-app/src for project proj_abc123

Esto crea specs desde la implementación actual — documentando lo que la app realmente hace. Estas specs se convierten en la línea base de paridad funcional.


Paso 2 — Analizar el Alcance de la Migración

Prompt:

Plan a migration from Express to Fastify for project proj_abc123

Qué esperar:

## Migration Analysis: Express 4 → Fastify 5

### Scope
- 23 route files
- 8 middleware (auth, logging, rate-limit, cors, compression, error, validation, cache)
- 4 error handlers
- 2 WebSocket integrations (socket.io → @fastify/websocket)
- 12 test files that mock express internals

### Equivalence Map
Express                    → Fastify
─────────────────────────────────────────
app.use(middleware)        → fastify.addHook / fastify.register(plugin)
app.get('/path', handler)  → fastify.get('/path', { schema }, handler)
req.body                   → request.body (same)
res.json(data)             → reply.send(data)
res.status(201).json(data) → reply.code(201).send(data)
next(err)                  → throw err (or return reply.send(err))
express-validator          → @fastify/ajv-compiler + JSON Schema
multer                     → @fastify/multipart
cors (package)             → @fastify/cors
helmet                     → @fastify/helmet
express-rate-limit         → @fastify/rate-limit
socket.io                  → @fastify/websocket + ws directly

### Risk Areas
- High: socket.io migration (no direct equivalent — behavior change)
- High: Middleware order (Fastify lifecycle differs from Express)
- Medium: Error handling (Fastify uses setErrorHandler, not error middleware)
- Low: Route parameters (compatible syntax)

### Estimated effort: 8–14 days
### Recommended approach: Strangler fig (route-by-route)

Paso 3 — Crear la Spec de Migración

Prompt:

Create a spec for migrating from Express to Fastify in project proj_abc123.
Approach: strangler fig — migrate route groups one at a time behind a reverse proxy.

Criterios de aceptación que incluirá la spec:

  1. Los 23 grupos de rutas se comportan de manera idéntica antes y después de la migración (suite de tests de paridad)
  2. Los esquemas de respuesta son validados con la integración AJV de Fastify
  3. El middleware de autenticación migrado a lifecycle hooks de Fastify (onRequest)
  4. Las respuestas de error mantienen la misma estructura JSON que la implementación de Express
  5. Funcionalidad WebSocket preservada (migración de socket.io a @fastify/websocket)
  6. Sin regresión en tiempo de respuesta — latencia P99 de Fastify ≤ P99 de Express × 1.05
  7. Todos los tests de integración existentes pasan contra el servidor Fastify
  8. Migración con cero downtime — Express y Fastify corren concurrentemente durante la transición

Paso 4 — Hacer Challenge del Plan de Migración

Prompt:

Challenge the migration spec for project proj_abc123

SpecForge indagará en casos borde:

  • ¿Qué pasa con las solicitudes en vuelo durante el corte Express → Fastify?
  • ¿Cómo se manejan las cookies de sesión durante el período de transición?
  • ¿Qué pasa si falla un feature flag y algunos usuarios llegan a Express, otros a Fastify?
  • ¿Hay algún middleware específico de Express sin equivalente en Fastify?

Paso 5 — Generar Tests de Paridad Funcional

Prompt:

Generate tests for the migration spec in project proj_abc123

Estos tests corren contra ambos el servidor Express y el Fastify — garantizando comportamiento idéntico:

typescript
// parity.test.ts
describe.each([
  ['Express', expressApp],
  ['Fastify', fastifyApp],
])('%s — POST /api/orders', (_, app) => {
  it('returns 201 with order ID on valid payload', async () => {
    const res = await request(app)
      .post('/api/orders')
      .send({ productId: 'prod_1', quantity: 2 })
    expect(res.status).toBe(201)
    expect(res.body).toMatchObject({ orderId: expect.any(String) })
  })

  it('returns 422 on missing productId', async () => {
    const res = await request(app)
      .post('/api/orders')
      .send({ quantity: 2 })
    expect(res.status).toBe(422)
  })
})

Paso 6 — Generar el Plan de Ejecución

Prompt:

Generate an execution plan for the migration spec in project proj_abc123

Cómo se ve un plan strangler fig:

markdown
## Fase 1: Fundación
- [ ] Instalar Fastify + plugins (fastify-cors, fastify-helmet, etc.)
- [ ] Configurar servidor Fastify junto a Express (puerto diferente)
- [ ] Configurar reverse proxy (nginx/Caddy) con enrutamiento por feature flag
- [ ] Escribir harness de tests de paridad que corra contra ambos servidores

## Fase 2: Migrar rutas (por grupo — se pueden paralelizar grupos)
- [ ] Grupo A: rutas /api/auth (bajo riesgo, sin WebSocket)
- [ ] Grupo B: rutas /api/users (riesgo medio, cambios de validación)
- [ ] Grupo C: rutas /api/orders (alto riesgo, integración de pagos)
- [ ] Grupo D: rutas /api/admin (bajo riesgo, solo interno)

## Fase 3: Migrar middleware
- [ ] Auth hook (Express middleware → Fastify onRequest)
- [ ] Error handler (next(err) → setErrorHandler)
- [ ] Rate limiting (@fastify/rate-limit)
- [ ] CORS + Helmet (@fastify/cors, @fastify/helmet)

## Fase 4: Migración WebSocket
- [ ] Reemplazar socket.io con @fastify/websocket + ws
- [ ] Migrar WebSocket del lado cliente: socket.io → WebSocket nativo
- [ ] Test de paridad: flujo de mensajes WebSocket

## Fase 5: Corte
- [ ] Cambiar reverse proxy a 100% Fastify
- [ ] Mantener Express corriendo por 48h (ventana de rollback)
- [ ] Eliminar Express después de 48h sin incidentes
- [ ] Actualizar PLAN.md y marcar spec de migración como done

Paso 7 — Ejecutar la Migración

Con cada fase:

  1. Implementar los cambios
  2. Ejecutar tests de paridad contra ambos servidores
  3. Validar paridad funcional con validate
  4. Detectar drift entre la spec de migración y la implementación

Prompt después de cada fase:

Validate the migration spec against the code at /Users/me/my-app/src for project proj_abc123

Paso 8 — Validación de Rendimiento

Prompt:

Audit the code at /Users/me/my-app/src for project proj_abc123

Verifica que la implementación de Fastify puntúe al menos tan bien como la línea base de Express en cumplimiento de arquitectura y calidad de código.

Para validación de latencia, ejecuta tu herramienta de load testing (k6, Artillery, wrk) contra ambos servidores y compara la latencia P99 — el criterio de la spec dice Fastify P99 ≤ Express P99 × 1.05.


Paso 9 — Documentar Decisiones Arquitectónicas

Prompt:

Generate ADRs for the migration spec in project proj_abc123

Esto produce:

  • ADR-001: Por qué strangler fig sobre migración big-bang
  • ADR-002: @fastify/websocket sobre socket.io en el contexto de Fastify
  • ADR-003: AJV JSON Schema sobre express-validator para validación de requests

Paso 10 — Capturar Aprendizajes

Prompt:

Capture learning: when migrating from Express to Fastify, middleware order matters more —
Fastify lifecycle hooks (onRequest, preHandler, onSend) must be planned before migration starts,
not discovered during implementation

Plan de Rollback

El enfoque strangler fig de SpecForge significa que el rollback siempre es posible:

FaseAcción de rollback
Durante la migraciónCambiar reverse proxy de vuelta a 100% Express
Después del corte (< 48h)Express sigue corriendo — cambiar proxy
Después de eliminar ExpressRestaurar desde el tag git pre-migration

Siempre tagea el último commit estable de Express antes de comenzar: git tag pre-fastify-migration.


Aplicar Esta Receta a Otras Migraciones

MigraciónPreocupación claveEnfoque recomendado
Django → FastAPIPatrones async, diferencias de ORMStrangler fig
Mongoose → PrismaMigración de esquema, API de queriesMódulo por módulo
React class → hooksParidad de comportamiento, lifecycleComponente por componente
PostgreSQL → CockroachDBDiferencias de dialecto SQLRead replica primero
REST → GraphQLBreaking change del lado clienteAditivo (mantener REST)
Node 18 → Node 22Deprecaciones de APIdetect_deprecations primero