Onze champs, trois routes, et huit applications qui lisent ce schéma sans jamais avoir été consultées avant un changement : c’est le point de départ d’une refonte qui a mal commencé chez un éditeur d’extensions WooCommerce dont l’API interne /wp-json/catalogue/v1/produits sert à la fois le back-office, l’application mobile et trois intégrations tierces. Le jour où un champ prix_ht a été renommé en prix_hors_taxe pour plus de clarté, deux consommateurs sur huit se sont arrêtés net.
Le problème n’est pas le changement lui-même : un schéma d’API doit évoluer. Le problème est l’absence de stratégie pour faire cohabiter l’ancien et le nouveau le temps que chaque consommateur s’adapte. Une suite de tests de contrat, déjà en place pour valider la forme des réponses, ne suffit pas si elle ne teste qu’une seule version à la fois.
Pourquoi une modification en place finit toujours par casser quelque chose
Modifier un schéma existant revient à parier que tous les consommateurs seront prévenus, testés et déployés en même temps. Dans une organisation avec plusieurs équipes ou plusieurs clients externes, ce pari est perdu d’avance. Un champ renommé, un type qui passe de chaîne à entier, une valeur null qui devient une chaîne vide : chacun de ces changements silencieux peut faire échouer un décodage JSON côté client sans qu’aucune alerte ne se déclenche côté serveur.
La tentation est de documenter le changement dans un CHANGELOG et d’espérer que chacun le lira à temps. Cela fonctionne rarement au-delà de deux ou trois consommateurs.
Structurer le schéma par version explicite
La première décision d’architecture consiste à ne jamais modifier un schéma déjà publié. Chaque version vit dans son propre espace de noms, avec ses propres classes de contrôleur REST et son propre schéma JSON :
inc/
api/
v1/
class-controller-produits.php
schema-produits.json
v2/
class-controller-produits.php
schema-produits.json
tests/
contract/
v1/
test-produits-schema.php
v2/
test-produits-schema.php
Cette arborescence a un coût : du code dupliqué entre v1 et v2. C’est un compromis assumé, pas un accident. Le contrôleur v2 peut très bien déléguer sa logique métier à une classe commune tout en gardant sa propre couche de sérialisation, celle qui décide exactement ce qui sort dans la réponse JSON.
Enregistrer les deux versions en parallèle
Avec l’API REST de WordPress, deux versions d’une même ressource s’enregistrent simplement sous des espaces de noms distincts via register_rest_route() :
add_action( 'rest_api_init', function () {
register_rest_route( 'catalogue/v1', '/produits', array(
'methods' => 'GET',
'callback' => 'catalogue_v1_lister_produits',
) );
register_rest_route( 'catalogue/v2', '/produits', array(
'methods' => 'GET',
'callback' => 'catalogue_v2_lister_produits',
) );
} );
Les deux routes coexistent tant que des consommateurs utilisent la v1. Aucune suppression tant qu’un test de contrat n’a pas confirmé, sur une période convenue à l’avance, qu’elle n’est plus appelée.

Tester chaque version indépendamment, jamais la dernière seulement
Une erreur fréquente consiste à faire évoluer la suite de tests de contrat en même temps que le schéma, si bien qu’elle ne protège plus que la version courante. Le dossier tests/contract/v1 doit rester figé et continuer à s’exécuter à chaque intégration continue, même des mois après l’introduction de la v2 :
- Le test v1 vérifie que
prix_htest toujours présent et de type numérique. - Le test v2 vérifie que
prix_hors_taxeest présent, avec le même type. - Un test de transition vérifie que les deux champs coexistent tant que la dépréciation n’est pas terminée.
Mesurer l’usage réel avant de couper
Avant de retirer une version, il faut savoir si elle est encore appelée. Un compteur simple, incrémenté à chaque appel et exposé via WP-CLI, donne une réponse honnête là où une intuition se trompe souvent :
wp catalogue api-usage --version=v1 --jours=30
Si ce compteur reste à zéro pendant la période convenue avec les équipes consommatrices, la suppression peut être planifiée sereinement, avec un test qui vérifie explicitement que la route v1 répond désormais 410 Gone plutôt que de disparaître silencieusement.
Une version d’API qui ne répond plus doit le dire clairement. Un 404 générique laisse croire à une panne ; un 410 documenté raconte une histoire compréhensible.
Documenter la dépréciation dans le schéma lui-même
Plutôt que de laisser la dépréciation vivre uniquement dans un wiki interne, elle peut être portée par le schéma JSON exposé par la route, via l’argument schema de register_rest_route(). Un champ deprecated à true sur prix_ht apparaît alors directement dans la réponse à une requête OPTIONS, consultable par n’importe quel consommateur qui prend la peine de vérifier.
| Approche | Avantage | Limite |
|---|---|---|
| Modification en place | Aucun code dupliqué | Casse tous les consommateurs simultanément |
| Versionnage par espace de noms | Cohabitation contrôlée, retrait mesuré | Code dupliqué entre versions |
| Champ additif uniquement | Aucune rupture possible | Le schéma grossit sans jamais se nettoyer |
En résumé
Un schéma d’API interne n’appartient jamais complètement à l’équipe qui l’écrit : il appartient aussi à chaque consommateur qui s’y est branché sans prévenir. Versionner par espace de noms, garder les tests de contrat anciens vivants et mesurer l’usage réel avant de couper une version : ces trois habitudes transforment une rupture brutale en une transition que personne ne remarque, ce qui est exactement l’objectif.