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:
- Crear una spec de paridad funcional antes de cualquier cambio de código
- Generar tests que verifican que el comportamiento coincide antes y después
- Proporcionar un plan de migración por fases con rollback en cada paso
- 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_abc123Esto 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_abc123Qué 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:
- Los 23 grupos de rutas se comportan de manera idéntica antes y después de la migración (suite de tests de paridad)
- Los esquemas de respuesta son validados con la integración AJV de Fastify
- El middleware de autenticación migrado a lifecycle hooks de Fastify (onRequest)
- Las respuestas de error mantienen la misma estructura JSON que la implementación de Express
- Funcionalidad WebSocket preservada (migración de socket.io a @fastify/websocket)
- Sin regresión en tiempo de respuesta — latencia P99 de Fastify ≤ P99 de Express × 1.05
- Todos los tests de integración existentes pasan contra el servidor Fastify
- 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_abc123SpecForge 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_abc123Estos 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_abc123Có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 donePaso 7 — Ejecutar la Migración
Con cada fase:
- Implementar los cambios
- Ejecutar tests de paridad contra ambos servidores
- Validar paridad funcional con
validate - 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_abc123Paso 8 — Validación de Rendimiento
Prompt:
Audit the code at /Users/me/my-app/src for project proj_abc123Verifica 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_abc123Esto 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 implementationPlan de Rollback
El enfoque strangler fig de SpecForge significa que el rollback siempre es posible:
| Fase | Acción de rollback |
|---|---|
| Durante la migración | Cambiar reverse proxy de vuelta a 100% Express |
| Después del corte (< 48h) | Express sigue corriendo — cambiar proxy |
| Después de eliminar Express | Restaurar 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ón | Preocupación clave | Enfoque recomendado |
|---|---|---|
| Django → FastAPI | Patrones async, diferencias de ORM | Strangler fig |
| Mongoose → Prisma | Migración de esquema, API de queries | Módulo por módulo |
| React class → hooks | Paridad de comportamiento, lifecycle | Componente por componente |
| PostgreSQL → CockroachDB | Diferencias de dialecto SQL | Read replica primero |
| REST → GraphQL | Breaking change del lado cliente | Aditivo (mantener REST) |
| Node 18 → Node 22 | Deprecaciones de API | detect_deprecations primero |