Les débats sur le versionnage d’API commencent presque toujours par le mauvais bout, c’est-à-dire par l’endroit où placer le numéro de version. C’est la décision la moins lourde de conséquences de tout le sujet. Ce qui compte, c’est de savoir quels changements exigent réellement une nouvelle version, et la plupart des équipes se trompent du côté de l’insouciance : elles livrent quelque chose qu’elles croient purement additif, et un client casse.
Le bon modèle mental est le suivant : votre API est une promesse sur ce qu’un appelant peut tenir pour acquis. Un changement casse la compatibilité s’il invalide quelque chose sur lequel un appelant raisonnable s’appuyait, et les appelants s’appuient sur bien plus que ce que votre documentation les autorise à faire.
Le changement qui piège tout le monde : ajouter un champ à une réponse. Il est additif, il ne peut pas casser un client bien écrit, et il casse pourtant de vrais clients de façon régulière, parce que certains valident les réponses de façon stricte et rejettent les champs inconnus. Savoir si la faute vous revient ou leur revient n’a plus aucune importance une fois leur intégration à l’arrêt et le téléphone qui sonne.
Ce qui casse vraiment la compatibilité
Sans ambiguïté, cela casse : supprimer ou renommer un champ, changer le type d’un champ, ajouter un paramètre obligatoire à la requête, durcir la validation, changer le sens d’une valeur existante, ou changer un code de statut sur lequel un appelant branche sa logique.
Cela casse en pratique, même si cela paraît sûr : ajouter un champ à une réponse quand les clients valident strictement. Changer l’ordre d’un tableau qui n’avait jamais été garanti mais que les appelants ont supposé. Changer un message d’erreur dont quelqu’un compare le texte comme une chaîne. Rendre asynchrone une opération synchrone.
Réellement sans risque : ajouter un nouveau paramètre de requête facultatif, ajouter un nouvel endpoint, assouplir la validation, et ajouter une valeur à une énumération, mais seulement si les clients ont été prévenus dès le départ d’attendre des valeurs inconnues et que vous pouvez vérifier qu’ils les traitent.
La règle sous-jacente est que la sûreté dépend de ce que les appelants font vraiment, pas de ce que la spécification autorise. Si vous maîtrisez tous les clients, vous pouvez le vérifier. Sinon, partez du principe que quelqu’un s’appuie sur ce que vous jugiez accessoire, parce que c’est le cas.
Stratégies de versionnage d’API et leur coût
La version dans l’URL. L’approche la plus répandue, et sa vertu est d’être évidente : /v1/orders et /v2/orders sont visiblement deux ressources différentes. Le coût, c’est qu’elle encourage les sauts de version de l’API entière, si bien qu’un changement touchant un seul endpoint entraîne tout le reste et oblige les clients à migrer d’un seul bloc.
La version dans un en-tête. Garde les URL stables et autorise une granularité plus fine, au prix de l’invisibilité. Personne ne voit la version dans un navigateur ou une ligne de log sans la chercher, et un appelant qui omet l’en-tête reçoit la valeur par défaut, choix que vous devez faire de façon délibérée.
Le versionnage par date. Un appelant se fixe sur une date et reçoit l’API telle qu’elle se comportait alors. Stripe documente cette approche, où chaque compte possède une version par défaut que chaque requête peut redéfinir. Elle donne les plus petits pas de migration possibles et déplace la charge de compatibilité dans votre propre base de code, qui maintient désormais des transformations entre versions.
Aucun versionnage, uniquement des ajouts. Viable et sous-estimé quand vous pouvez vous engager sincèrement à ne jamais rien retirer. Le coût, ce sont des champs que vous ne pouvez plus supprimer et des comportements que vous ne pouvez plus corriger : un impôt lent plutôt qu’une facture brutale.
Il n’y a pas de bonne réponse, seulement un arbitrage entre votre charge de migration et celle de vos clients. Le versionnage par date est le plus doux pour les appelants et le plus coûteux à exploiter. Le versionnage dans l’URL fait exactement l’inverse.
Rendre les ruptures supportables
Étendre, puis contracter. Ajoutez le nouveau champ à côté de l’ancien. Alimentez les deux. Laissez aux clients le temps de basculer. Retirez l’ancien dans une version ultérieure. Cela transforme une rupture en deux changements sûrs, et l’étape supplémentaire en vaut la peine presque à chaque fois.
Mesurez qui utilise quoi. Vous ne pouvez pas retirer une version sans risque sans savoir qui s’en sert encore. Journalisez la version et l’identité du client à chaque requête, pour que la dépréciation devienne une conversation appuyée sur des faits plutôt qu’une annonce lancée dans le vide.
Annoncez par un mécanisme, pas seulement par courriel. Les en-têtes de réponse Deprecation et Sunset permettent à un client de découvrir la date de retrait par programme, ce qui a plus de chances d’être remarqué qu’un message envoyé à une adresse que plus personne ne lit.
Donnez un délai réaliste. Les intégrations sont maintenues par des gens qui ont d’autres priorités, et une échéance plus courte que leur cycle de livraison sera tout simplement manquée. Six mois est la norme pour une API publique ; moins se défend pour un petit groupe de partenaires connus à qui vous avez parlé.
Retirer une ancienne version
C’est la mesure décrite plus haut qui rend l’opération possible. Annoncez la date, observez le trafic décliner, et contactez nommément les appelants qui n’ont pas bougé.
Attendez-vous à une longue traîne. Il existera des intégrations dont plus personne chez le client ne se souvient d’être propriétaire, et on les découvrira le jour où vous couperez la version. Les coupures partielles aident : désactivez brièvement l’ancienne version à des heures annoncées avant la date finale, pour que la panne survienne pendant que quelqu’un l’attend et non en pleine période chargée.
La position honnête consiste à admettre que certains appelants ne bougeront que le jour où l’ancienne version cessera de fonctionner. Prévoyez-le au lieu d’en être surpris, et assurez-vous que le mode de défaillance soit une erreur claire expliquant ce qui s’est passé, pas un délai d’attente qui expire.
La version dont vous n’avez pas besoin
La plupart des API internes n’ont besoin d’aucun versionnage, parce que vous maîtrisez chaque appelant et pouvez changer les deux côtés ensemble. Ajouter une négociation de version à une interface utilisée par deux de vos propres services, c’est de la mécanique qui coûte quelque chose et ne protège de rien.
Le moment où cela devient nécessaire, c’est celui où vous ne pouvez plus déployer tous les consommateurs en même temps, qu’ils appartiennent à une autre équipe, à un autre cycle de livraison ou à une autre entreprise. Voilà le vrai déclencheur, et il n’a rien à voir avec le caractère public de l’API. Les considérations de conception de notre guide des API serverless s’appliquent de la même manière à la périphérie.
Mecanik conçoit et maintient ce type d’API dans le cadre de nos prestations de développement logiciel. Le schéma de version est rarement la décision intéressante ; savoir lequel de vos appelants est encore sur l’ancienne l’est toujours.
Questions fréquentes
Qu’est-ce qui constitue un changement d’API cassant ? Supprimer ou renommer un champ, changer le type d’un champ, ajouter un paramètre obligatoire, durcir la validation, changer le sens d’une valeur, ou changer un code de statut sur lequel les appelants branchent leur logique. Cassent aussi en pratique : ajouter un champ de réponse quand les clients valident strictement, changer l’ordre d’un tableau que les appelants supposaient, et modifier le texte d’un message d’erreur que quelqu’un compare.
Faut-il mettre la version d’API dans l’URL ou dans un en-tête ? Le versionnage dans l’URL est visible et simple, mais il encourage les sauts de l’API entière et force les clients à tout migrer d’un coup. Le versionnage par en-tête garde les URL stables et autorise une granularité plus fine, mais reste invisible dans les logs et les navigateurs, et exige une valeur par défaut délibérée pour les appelants qui l’omettent. Aucun des deux n’est faux ; ils arbitrent votre charge de migration contre celle de vos clients.
Qu’est-ce que le versionnage d’API par date ? Un appelant se fixe sur une date et reçoit l’API telle qu’elle se comportait alors, comme le fait Stripe avec une version par défaut par compte que chaque requête peut redéfinir. Cela donne aux clients les plus petits pas de migration possibles et déplace la charge de compatibilité dans votre base de code, qui maintient ensuite des transformations entre versions.
Quelle durée donner à une dépréciation d’API ? Six mois est la norme pour une API publique, et moins se défend pour un petit groupe de partenaires connus à qui vous avez parlé directement. Les intégrations sont maintenues par des gens qui ont d’autres priorités, donc un délai plus court que leur cycle de livraison sera manqué, quelle que soit la clarté de l’annonce.
Les API internes ont-elles besoin d’un versionnage ? En général non, parce que vous maîtrisez chaque appelant et pouvez changer les deux côtés ensemble. Le versionnage devient nécessaire au moment où vous ne pouvez plus déployer tous les consommateurs en même temps, qu’ils appartiennent à une autre équipe, à un autre cycle de livraison ou à une autre entreprise. C’est cela le déclencheur, pas le fait que l’API soit publique.
Commentaires