Versionnement d'API et rétrocompatibilité
Concevoir des stratégies de versionnement d'API qui évoluent gracieusement sans casser les consommateurs existants.
Déclencheurs d'activation
Activer sur : "versionnement d'API", "rétrocompatibilité", "dépréciation", "changement cassant", "migration d'API", "v1 v2", "en-tête sunset", "évolution d'API", "changement non cassant"
PAS pour : Évolution de schéma de données → schema-evolution-manager | Routage de version de passerelle → api-gateway-reverse-proxy-expert | Dépréciation GraphQL → graphql-server-architect
Démarrage rapide
- Classer le changement — additif (sûr), modification (peut-être cassant), suppression (cassant)
- Choisir une stratégie — chemin d'URL (
/v2/), en-tête (API-Version), ou négociation de contenu - Implémenter les en-têtes Sunset — RFC 8594 informe les consommateurs quand les anciennes versions expirent
- Exécuter les versions en parallèle — période de chevauchement d'au moins 6 mois pour les versions majeures
- Surveiller l'adoption — suivre le trafic par version pour savoir quand retirer une version
Capacités principales
| Domaine | Technologies |
|---|---|
| Versionnement d'URL | Routage basé sur le chemin /api/v1/, /api/v2/ |
| Versionnement par en-tête | API-Version: 2024-01-15, Accept-Version |
| Négociation de contenu | Accept: application/vnd.myapi.v2+json |
| Dépréciation | En-tête Sunset (RFC 8594), en-tête Deprecation |
| Outillage | OpenAPI 3.1 overlays, Optic, Bump.sh |
Patrons architecturaux
Arbre de décision de stratégie de versionnement
Est-ce uniquement additif ? (nouveaux champs, nouveaux endpoints)
├─ OUI → Pas de changement de version nécessaire (rétrocompatible)
└─ NON → S'agit-il d'un renommage/type de champ ?
├─ OUI → Pouvez-vous conserver les champs anciens + nouveaux ?
│ ├─ OUI → Ajouter le nouveau, déprécier l'ancien (version mineure)
│ └─ NON → Changement de version majeure (v1 → v2)
└─ NON → S'agit-il d'une suppression ?
└─ OUI → Changement de version majeure avec période de dépréciation
Déploiement parallèle de versions
// Routeur Express avec routage basé sur la version
import { Router } from 'express';
const v1Router = Router();
const v2Router = Router();
// v1 : retourne { name: string }
v1Router.get('/users/:id', async (req, res) => {
const user = await getUser(req.params.id);
res.json({ name: user.fullName }); // forme héritée
});
// v2 : retourne { firstName, lastName, displayName }
v2Router.get('/users/:id', async (req, res) => {
const user = await getUser(req.params.id);
res.json({
firstName: user.firstName,
lastName: user.lastName,
displayName: user.fullName,
});
});
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);
// Middleware d'en-tête Sunset pour v1
v1Router.use((req, res, next) => {
res.set('Sunset', 'Sat, 01 Nov 2026 00:00:00 GMT');
res.set('Deprecation', 'true');
res.set('Link', '</api/v2>; rel="successor-version"');
next();
});
Versionnement par date (modèle Stripe)
API-Version: 2026-03-15
Les modifications sont liées à des dates, et non à des entiers :
2026-01-01 → base
2026-03-15 → `name` renommé en `display_name`
2026-06-01 → `legacy_field` supprimé
Le serveur épingle les requêtes non versionnées à la version par défaut du compte.
Chaque version est une couche de transformation sur le modèle interne canonique.
Anti-patrons
- Versionnement trop précoce — la plupart des changements sont additifs et ne nécessitent pas de changement de version ; ajoutez des champs librement
- Pas de calendrier de dépréciation — déprécier sans date d'arrêt concrète signifie que les versions vivent éternellement
- Copier des contrôleurs entiers — utilisez une couche de transformation/adaptateur, pas une logique métier dupliquée par version
- Ignorer les champs inconnus — les API doivent être des lecteurs tolérants ; ignorez les champs inconnus en entrée (loi de Postel)
- Changements cassants dans les versions mineures — si les consommateurs dépendaient de quelque chose, le retirer est cassant quelle que soit votre intention
Checklist qualité
- Les changements additifs (nouveaux champs, nouveaux endpoints) sont livrés sans changement de version
- Les changements cassants nécessitent une version majeure avec une période de dépréciation d'au moins 6 mois
- L'en-tête Sunset (RFC 8594) est défini sur les versions dépréciées
- L'en-tête Link pointe les consommateurs vers la version successeur
- Le trafic par version est surveillé pour suivre la progression de la migration
- La spécification OpenAPI est publiée par version avec des outils de diff (Optic/Bump.sh)
- Plan de notification aux consommateurs : journal des modifications, e-mail, avertissement dans le tableau de bord
- Le routage de version est géré au niveau de la passerelle, pas dans le code d'application
- Les tests d'intégration valident la rétrocompatibilité (tests de contrat)