
Introduction
Imaginez.
Vous êtes une dev assez expérimentée et vous fabriquez un logiciel destiné aux bibliothèques municipales.
Par ailleurs, vous encadrez d’autres personnes plus juniors.
L’une d’entre elles (appelons-là Camille) a eu pour tâche d’exposer une API qui permet de gérer les fonds des bibliothèques.
C’est l’heure de la revue de code, et Camille vous montre une API qui ressemble à ceci :
Comme vous avez déjà été confrontée au problème des mises à jour d’API, vous dites à Camille de rajouter un numéro de version au début de chaque route, comme ceci :
"Ainsi", expliquez-vous à Camille, "le jour où on change d'avis sur l'API, on pourra faire une v2 sans casser l'ancien code".
"Oh je comprends", répond Camille.
Quelques temps plus tard, après s'être assurée que la nouvelle API est correctement documentée, vous la déployez et la présentez aux autres équipes.
L'API a beaucoup de succès, et quelques mois plus tard c'est au moins 3 équipes différentes qui utilisent votre API régulièrement : bien joué !
La tentation de la v2
Le temps a passé, et Camille revient vous voir :
"Dis, j'ai réfléchi et j'ai eu des idées pour améliorer l'API. Par exemple, la route api/book/<isbn> n'est pas très cohérente. On documente qu'il faut passer l'ISBN directement dans le chemin de la route, mais on renvoie un JSON avec un champ id":
"ça cause pas mal de confusion, et puis c'est déjà arrivé que les ISBN changent de format. Du coup, vu qu'on a bien pris soin de faire une v1, est-ce que ça ne serait pas l'occasion de faire une V2 ?
On aurait juste GET /api/v2/book et on pourrait passer le ISBN dans les paramètres de la query. On en profite pour remplacer le champ id par un champ isbn:"
Qu'est-ce que tu en penses?"
Spontanément, vous avez envie de dire oui : après tout, l'API V2 a l'air bien plus propre.
Mais d'un autre côté, vous hésitez. L'API V1 est déjà en production. Certes, il y a bien eu quelques bugs liés à la gestion des IDs, mais ils ont tous été corrigés depuis.
Pourquoi ne PAS faire une v2
Vous décidez de ne pas prendre de décision tout de suite, et à la place vous vous posez avec Camille pour une séance de brainstorming.
À la sortie de la réunion, vous vous êtes mises d'accord sur le fait que le scénario le plus probable est le suivant:
- La v2 est déployée en parallèle de la v1 (de façon à ne pas casser la prod)
- Les autres équipes savent qu'il faudra faire le passage de v1 à v2, mais il n'y a pas de caractère d'urgence.
- Chaque équipe se retrouve donc avec un ticket "mettre à jour vers la v2" dans son backlog
- Ces tickets considérés (à raison) par le métier comme étant purement techniques et ne sont jamais priorisés
- Quelques équipes (mais pas toutes) mettent à jour parce que la v2 leur apporte de vrais avantages - ou bien elles commencent à utiliser l’API directement en version 2
- Il y a un bug en production facile à corriger en v2 mais pas en v1 et comme vous ne pouvez pas forcer les équipes à mettre à jour, vous devez faire deux correctifs différents, les tester et les déployer sans rien casser.
Bref. Beaucoup d’ennuis en perspective …
Que faire à la place ?
Et bien, ne jamais faire de 2.0, tout simplement 😊
L'idée est de considérer qu'une fois que la 1.0 est publique, c'est votre travail de ne jamais casser la rétrocompatibilité
Dans le cas qui nous occupe, vous pouvez tout à fait garder la route "legacy" GET /api/v1/book/<isbn> en plus de la route qui prend l'id en paramètre.
Et de la même façon, vous pouvez renvoyer un JSON qui contient les deux champs:
Si on imagine que le backend est en Python/Flask, cela donnerait:
Pour un client TypeScript qui ne s'est jamais mis à jour :
Et pour un client qui est passé aux API recommandées :
Comment tester ?
Vous pouvez utiliser des tests de contrat :
Notez qu'on n'utilise surtout pas les objets dataclass déclarés au niveau du back-end dans les tests, parce qu'on veutque ces tests cassent si jamais l'API devient incompatible !
Conclusion
J’espère que vous avez apprécié cet exemple. Il n’est pas si fictif que cela, on peut penser notamment à Python, dont le passage de 2 à 3 a causé a duré près de 10 ans (!) et a causé pas mal de souci à la communauté.
Et on peut aussi citer Rust et Go, qui ont sorti leur version 1.0 en 2012 et 2015 respectivement et n’ont jamais cassé leur rétrocompatibilité.
En tout cas, j’espère que vous cela vous aura donné à réfléchir, et que vous vous poserez davantage de questions avant de proposer une mise à jour incompatible aux personnes qui utilisent vos APIs 🙂


.png)

.png)

