Receita: Migração de Stack Tecnológica
Esta receita percorre o planejamento e a execução de uma migração de tecnologia usando o SpecForge. Usaremos a migração de uma API Node.js de Express 4 para Fastify como exemplo — uma migração comum que toca rotas, middlewares, plugins e tratamento de erros.
O mesmo fluxo de trabalho se aplica a qualquer migração de tecnologia: upgrades de framework, mudanças de banco de dados, substituições de ORM ou upgrades de linguagem.
Antes de Começar
Migrações são de alto risco porque tocam muitos arquivos e podem quebrar comportamentos de formas sutis. O SpecForge reduz esse risco ao:
- Criar uma spec de paridade funcional antes de qualquer mudança de código
- Gerar testes que verificam se o comportamento corresponde antes e depois
- Fornecer um plano de migração faseado com rollback em cada etapa
- Detectar drift conforme a migração progride
Etapa 1 — Analisar a Base de Código Fonte
Prompt:
Faça engenharia reversa da minha base de código em /Users/me/meu-app/src para o projeto proj_abc123Isso cria specs da implementação atual — documentando o que o app realmente faz. Essas specs se tornam a linha de base de paridade funcional.
Etapa 2 — Analisar o Escopo da Migração
Prompt:
Planeje uma migração do Express para o Fastify para o projeto proj_abc123O que esperar:
## Análise de Migração: Express 4 → Fastify 5
### Escopo
- 23 arquivos de rotas
- 8 middlewares (auth, logging, rate-limit, cors, compression, error, validation, cache)
- 4 handlers de erro
- 2 integrações WebSocket (socket.io → @fastify/websocket)
- 12 arquivos de teste que mockam internos do express
### Mapa de Equivalências
Express → Fastify
─────────────────────────────────────────
app.use(middleware) → fastify.addHook / fastify.register(plugin)
app.get('/path', handler) → fastify.get('/path', { schema }, handler)
req.body → request.body (mesmo)
res.json(data) → reply.send(data)
res.status(201).json(data) → reply.code(201).send(data)
next(err) → throw err (ou return reply.send(err))
express-validator → @fastify/ajv-compiler + JSON Schema
multer → @fastify/multipart
cors (pacote) → @fastify/cors
helmet → @fastify/helmet
express-rate-limit → @fastify/rate-limit
socket.io → @fastify/websocket + ws diretamente
### Áreas de Risco
- Alto: migração de socket.io (sem equivalente direto — mudança de comportamento)
- Alto: Ordem de middleware (ciclo de vida do Fastify difere do Express)
- Médio: Tratamento de erros (Fastify usa setErrorHandler, não middleware de erro)
- Baixo: Parâmetros de rota (sintaxe compatível)
### Esforço estimado: 8–14 dias
### Abordagem recomendada: Strangler fig (rota por rota)Etapa 3 — Criar a Spec de Migração
Prompt:
Crie uma spec para migração do Express para Fastify no projeto proj_abc123.
Abordagem: strangler fig — migrar grupos de rotas um de cada vez por trás de um proxy reverso.Critérios de aceitação que a spec incluirá:
- Todos os 23 grupos de rotas se comportam identicamente antes e após a migração (suite de testes de paridade)
- Schemas de resposta são validados com a integração AJV nativa do Fastify
- Middleware de autenticação migrado para hooks do ciclo de vida do Fastify (onRequest)
- Respostas de erro mantêm a mesma estrutura JSON da implementação Express
- Funcionalidade WebSocket preservada (migração de socket.io para @fastify/websocket)
- Sem regressão no tempo de resposta — latência P99 do Fastify ≤ P99 do Express × 1,05
- Todos os testes de integração existentes passam no servidor Fastify
- Migração sem downtime — Express e Fastify rodam simultaneamente durante a transição
Etapa 4 — Testar o Plano de Migração
Prompt:
Desafie a spec de migração do projeto proj_abc123O SpecForge investigará casos extremos:
- O que acontece com requisições em andamento durante a transição Express → Fastify?
- Como os cookies de sessão são tratados durante o período de transição?
- E se um feature flag falhar e alguns usuários acessem o Express, outros o Fastify?
- Há algum middleware específico do Express sem equivalente no Fastify?
Etapa 5 — Gerar Testes de Paridade Funcional
Prompt:
Gere testes para a spec de migração no projeto proj_abc123Esses testes rodam contra ambos os servidores Express e Fastify — garantindo comportamento 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)
})
})Etapa 6 — Gerar o Plano de Execução
Prompt:
Gere um plano de execução para a spec de migração no projeto proj_abc123Como fica um plano strangler fig:
markdown
## Fase 1: Fundação
- [ ] Instalar Fastify + plugins (fastify-cors, fastify-helmet, etc.)
- [ ] Configurar servidor Fastify junto ao Express (porta diferente)
- [ ] Configurar proxy reverso (nginx/Caddy) com roteamento por feature flag
- [ ] Escrever harness de testes de paridade que roda contra ambos os servidores
## Fase 2: Migrar rotas (por grupo — pode paralelizar grupos)
- [ ] Grupo A: rotas /api/auth (baixo risco, sem WebSocket)
- [ ] Grupo B: rotas /api/users (risco médio, mudanças de validação)
- [ ] Grupo C: rotas /api/orders (alto risco, integração de pagamento)
- [ ] Grupo D: rotas /api/admin (baixo risco, apenas interno)
## Fase 3: Migrar middlewares
- [ ] Hook de auth (middleware Express → Fastify onRequest)
- [ ] Handler de erro (next(err) → setErrorHandler)
- [ ] Rate limiting (@fastify/rate-limit)
- [ ] CORS + Helmet (@fastify/cors, @fastify/helmet)
## Fase 4: Migração WebSocket
- [ ] Substituir socket.io por @fastify/websocket + ws
- [ ] Migrar socket.io do lado do cliente → WebSocket nativo
- [ ] Teste de paridade: fluxo de mensagens WebSocket
## Fase 5: Cutover
- [ ] Trocar proxy reverso para 100% Fastify
- [ ] Manter Express rodando por 48h (janela de rollback)
- [ ] Remover Express após 48h sem incidentes
- [ ] Atualizar PLAN.md e marcar spec de migração como doneEtapa 7 — Executar a Migração
Com cada fase:
- Implemente as mudanças
- Execute testes de paridade contra ambos os servidores
- Valide paridade funcional com
validate - Detecte drift entre a spec de migração e a implementação
Prompt após cada fase:
Valide a spec de migração contra o código em /Users/me/meu-app/src para o projeto proj_abc123Etapa 8 — Validação de Performance
Prompt:
Audite o código em /Users/me/meu-app/src para o projeto proj_abc123Verifique se a implementação Fastify pontua pelo menos tão bem quanto a baseline Express em conformidade com a arquitetura e qualidade de código.
Para validação de latência, execute sua ferramenta de teste de carga (k6, Artillery, wrk) contra ambos os servidores e compare a latência P99 — o critério da spec diz que P99 Fastify ≤ P99 Express × 1,05.
Etapa 9 — Documentar Decisões Arquiteturais
Prompt:
Gere ADRs para a spec de migração no projeto proj_abc123Isso produz:
- ADR-001: Por que strangler fig em vez de migração big-bang
- ADR-002: @fastify/websocket em vez de socket.io no contexto Fastify
- ADR-003: JSON Schema AJV em vez de express-validator para validação de requisições
Etapa 10 — Capturar Aprendizados
Prompt:
Capture aprendizado: ao migrar do Express para o Fastify, a ordem dos middlewares importa mais —
os hooks do ciclo de vida do Fastify (onRequest, preHandler, onSend) devem ser planejados antes do início da migração,
não descobertos durante a implementaçãoPlano de Rollback
A abordagem strangler fig do SpecForge significa que o rollback é sempre possível:
| Fase | Ação de rollback |
|---|---|
| Durante a migração | Trocar proxy reverso de volta para 100% Express |
| Após cutover (< 48h) | Express ainda está rodando — trocar proxy |
| Após remoção do Express | Restaurar a partir da tag git pre-migration |
Sempre faça tag do último commit Express estável antes de começar: git tag pre-fastify-migration.
Aplicando Esta Receita a Outras Migrações
| Migração | Preocupação principal | Abordagem recomendada |
|---|---|---|
| Django → FastAPI | Padrões assíncronos, diferenças de ORM | Strangler fig |
| Mongoose → Prisma | Migração de schema, API de consultas | Módulo por módulo |
| React class → hooks | Paridade comportamental, ciclo de vida | Componente por componente |
| PostgreSQL → CockroachDB | Diferenças de dialeto SQL | Réplica de leitura primeiro |
| REST → GraphQL | Breaking change no cliente | Aditivo (manter REST) |
| Node 18 → Node 22 | Depreciações de API | detect_deprecations primeiro |