Recette : Migration de stack technologique
Cette recette décrit la planification et l'exécution d'une migration technologique avec SpecForge. Nous utiliserons la migration d'une API Node.js d'Express 4 vers Fastify comme exemple — une migration courante qui touche le routage, les middlewares, les plugins et la gestion des erreurs.
Le même flux de travail s'applique à toute migration technologique : mises à niveau de framework, changements de moteur de base de données, remplacement d'ORM ou mises à niveau de langage.
Avant de commencer
Les migrations sont à haut risque car elles touchent de nombreux fichiers et peuvent casser le comportement de façon subtile. SpecForge réduit ce risque en :
- Créant une spec de parité fonctionnelle avant tout changement de code
- Générant des tests qui vérifient que le comportement correspond avant et après
- Fournissant un plan de migration par phases avec rollback à chaque étape
- Détectant le drift au fur et à mesure de l'avancement de la migration
Étape 1 — Analyser la base de code source
Prompt :
Rétro-ingénieure ma base de code dans /Users/moi/mon-app/src pour le projet proj_abc123Cela crée des specs depuis l'implémentation actuelle — documentant ce que l'application fait réellement. Ces specs deviennent la baseline de parité fonctionnelle.
Étape 2 — Analyser le périmètre de la migration
Prompt :
Planifie une migration d'Express vers Fastify pour le projet proj_abc123Ce à quoi s'attendre :
## Analyse de migration : Express 4 → Fastify 5
### Périmètre
- 23 fichiers de routes
- 8 middlewares (auth, logging, rate-limit, cors, compression, error, validation, cache)
- 4 gestionnaires d'erreurs
- 2 intégrations WebSocket (socket.io → @fastify/websocket)
- 12 fichiers de tests mockant les internals express
### Carte d'équivalences
Express → Fastify
─────────────────────────────────────────
app.use(middleware) → fastify.addHook / fastify.register(plugin)
app.get('/path', handler) → fastify.get('/path', { schema }, handler)
req.body → request.body (identique)
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 (package) → @fastify/cors
helmet → @fastify/helmet
express-rate-limit → @fastify/rate-limit
socket.io → @fastify/websocket + ws directement
### Zones à risque
- Élevé : migration socket.io (pas d'équivalent direct — changement de comportement)
- Élevé : ordre des middlewares (le cycle de vie Fastify diffère d'Express)
- Moyen : gestion des erreurs (Fastify utilise setErrorHandler, pas le middleware d'erreur)
- Faible : paramètres de routes (syntaxe compatible)
### Effort estimé : 8–14 jours
### Approche recommandée : strangler fig (route par route)Étape 3 — Créer la spec de migration
Prompt :
Crée une spec pour migrer d'Express vers Fastify dans le projet proj_abc123.
Approche : strangler fig — migrer des groupes de routes un à la fois derrière un reverse proxy.Critères d'acceptation que la spec inclura :
- Les 23 groupes de routes se comportent de façon identique avant et après la migration (suite de tests de parité)
- Les schémas de réponse sont validés avec l'intégration AJV native de Fastify
- Le middleware d'authentification migré vers les lifecycle hooks Fastify (onRequest)
- Les réponses d'erreur maintiennent la même structure JSON que l'implémentation Express
- La fonctionnalité WebSocket est préservée (migration de socket.io vers @fastify/websocket)
- Pas de régression en temps de réponse — latence P99 Fastify ≤ latence P99 Express × 1,05
- Tous les tests d'intégration existants passent contre le serveur Fastify
- Migration sans interruption — Express et Fastify fonctionnent simultanément pendant la transition
Étape 4 — Tester le plan de migration sous pression
Prompt :
Challenge la spec de migration pour le projet proj_abc123SpecForge sondera les cas limites :
- Que se passe-t-il pour les requêtes en vol lors du basculement Express → Fastify ?
- Comment les cookies de session sont-ils gérés pendant la période de transition ?
- Que se passe-t-il si un feature flag échoue et que certains utilisateurs touchent Express, d'autres Fastify ?
- Y a-t-il des middlewares spécifiques Express n'ayant pas d'équivalent Fastify ?
Étape 5 — Générer les tests de parité fonctionnelle
Prompt :
Génère des tests pour la spec de migration dans le projet proj_abc123Ces tests s'exécutent contre les deux serveurs Express et Fastify — assurant un comportement identique :
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)
})
})Étape 6 — Générer le plan d'exécution
Prompt :
Génère un plan d'exécution pour la spec de migration dans le projet proj_abc123À quoi ressemble un plan strangler fig :
markdown
## Phase 1 : Fondation
- [ ] Installer Fastify + plugins (fastify-cors, fastify-helmet, etc.)
- [ ] Configurer le serveur Fastify en parallèle d'Express (port différent)
- [ ] Configurer le reverse proxy (nginx/Caddy) avec routage par feature flag
- [ ] Écrire le harnais de tests de parité qui s'exécute contre les deux serveurs
## Phase 2 : Migrer les routes (par groupe — peut paralléliser les groupes)
- [ ] Groupe A : routes /api/auth (risque faible, pas de WebSocket)
- [ ] Groupe B : routes /api/users (risque moyen, changements de validation)
- [ ] Groupe C : routes /api/orders (risque élevé, intégration paiement)
- [ ] Groupe D : routes /api/admin (risque faible, interne uniquement)
## Phase 3 : Migrer les middlewares
- [ ] Hook auth (middleware Express → Fastify onRequest)
- [ ] Gestionnaire d'erreurs (next(err) → setErrorHandler)
- [ ] Rate limiting (@fastify/rate-limit)
- [ ] CORS + Helmet (@fastify/cors, @fastify/helmet)
## Phase 4 : Migration WebSocket
- [ ] Remplacer socket.io par @fastify/websocket + ws
- [ ] Migrer côté client socket.io → WebSocket natif
- [ ] Test de parité : flux de messages WebSocket
## Phase 5 : Basculement
- [ ] Basculer le reverse proxy à 100% Fastify
- [ ] Garder Express en fonctionnement pendant 48h (fenêtre de rollback)
- [ ] Supprimer Express après 48h sans incident
- [ ] Mettre à jour PLAN.md et marquer la spec de migration comme doneÉtape 7 — Exécuter la migration
À chaque phase :
- Implémenter les changements
- Exécuter les tests de parité contre les deux serveurs
- Valider la parité fonctionnelle avec
validate - Détecter le drift entre la spec de migration et l'implémentation
Prompt après chaque phase :
Valide la spec de migration contre le code dans /Users/moi/mon-app/src pour le projet proj_abc123Étape 8 — Validation des performances
Prompt :
Audite le code dans /Users/moi/mon-app/src pour le projet proj_abc123Vérifiez que l'implémentation Fastify obtient au moins aussi bien que la baseline Express sur la conformité architecturale et la qualité du code.
Pour la validation de latence, exécutez votre outil de test de charge (k6, Artillery, wrk) contre les deux serveurs et comparez la latence P99 — le critère de la spec dit que Fastify P99 ≤ Express P99 × 1,05.
Étape 9 — Documenter les décisions architecturales
Prompt :
Génère des ADR pour la spec de migration dans le projet proj_abc123Cela produit :
- ADR-001 : Pourquoi strangler fig plutôt qu'une migration big-bang
- ADR-002 : @fastify/websocket plutôt que socket.io dans le contexte Fastify
- ADR-003 : AJV JSON Schema plutôt qu'express-validator pour la validation des requêtes
Étape 10 — Capturer les apprentissages
Prompt :
Capture learning : lors de la migration d'Express vers Fastify, l'ordre des middlewares importe davantage —
les lifecycle hooks Fastify (onRequest, preHandler, onSend) doivent être planifiés avant le début de la migration,
pas découverts pendant l'implémentationPlan de rollback
L'approche strangler fig de SpecForge signifie que le rollback est toujours possible :
| Phase | Action de rollback |
|---|---|
| Pendant la migration | Basculer le reverse proxy à 100% Express |
| Après le basculement (< 48h) | Express est toujours en fonctionnement — changer le proxy |
| Après la suppression d'Express | Restaurer depuis le tag git pre-migration |
Taguez toujours le dernier commit Express stable avant de commencer : git tag pre-fastify-migration.
Appliquer cette recette à d'autres migrations
| Migration | Préoccupation principale | Approche recommandée |
|---|---|---|
| Django → FastAPI | Patterns async, différences ORM | Strangler fig |
| Mongoose → Prisma | Migration de schéma, API de requête | Module par module |
| React classes → hooks | Parité comportementale, cycle de vie | Composant par composant |
| PostgreSQL → CockroachDB | Différences de dialecte SQL | Réplica de lecture en premier |
| REST → GraphQL | Changement cassant côté client | Additif (garder REST) |
| Node 18 → Node 22 | Dépréciations d'API | detect_deprecations en premier |