Pact et les Consumer-Driven Contracts : puissance, limites et usage réel
Article Bonus
Cet article est un complément approfondi de la série sur les tests d'API. Si vous êtes arrivé jusqu'ici, c'est que vous vous intéressez sérieusement aux tests de contrat et que vous voulez comprendre Pact en profondeur.
Pact est devenu le standard du consumer-driven contract testing, mais c'est aussi l'un des outils les plus mal compris de l'écosystème des tests. Beaucoup d'équipes l'adoptent avec enthousiasme, puis l'abandonnent quelques mois plus tard. Ce n'est pas la faute de Pact, c'est un problème de compréhension et d'attentes.
Pourquoi les architectures distribuées rendent les contrats indispensables
Avant de plonger dans Pact, rappelons pourquoi les contrats sont devenus critiques. Dans une application monolithique, votre IDE vous alerte immédiatement si vous cassez une dépendance. Avec quinze microservices développés par dix équipes différentes, c'est impossible.
Le service utilisateur expose une API consommée par les services de commande, notification et analytics. Quand l'équipe utilisateur change le format d'un champ, comment sait-elle qu'elle ne casse pas les trois consommateurs ? La réponse traditionnelle "on communique beaucoup" ne scale pas.
Les contrats créent un système de feedback automatique. Chaque consommateur définit explicitement ce dont il a besoin. Le producteur vérifie automatiquement qu'il satisfait tous ces besoins avant de déployer. Si un changement casse un consommateur, vous le savez immédiatement. C'est comme avoir un compilateur distribué qui vérifie la compatibilité entre services.
Contrat versus validation de schéma : le rappel essentiel
Beaucoup d'équipes confondent encore contrat et validation de schéma. Clarifions ça une dernière fois.
La validation de schéma vérifie qu'une réponse respecte un format défini. Elle s'assure qu'un champ userId est de type string, qu'un champ price est un nombre. C'est unidirectionnel : le producteur dit "voici la structure" et vérifie qu'il la respecte.
Un contrat va plus loin. Il capture les attentes réelles du consommateur. Le consommateur dit "voici exactement ce dont j'ai besoin", et le producteur prouve qu'il peut le fournir. C'est bidirectionnel : les deux parties s'accordent sur ce qui est échangé.
Exemple : votre API retourne un status qui peut être "active" ou "inactive". La validation vérifie que c'est une string. Un contrat vérifie que le consommateur gère bien ces deux valeurs ET que le producteur ne va pas en ajouter une troisième sans prévenir.
Cette distinction est cruciale parce que si vous attendez de Pact qu'il fasse de la simple validation de schéma, vous serez déçu. Pact gère des contrats riches qui capturent les interactions réelles.
Producer-driven versus Consumer-driven : deux philosophies radicalement différentes
Maintenant, parlons de qui définit le contrat. Cette différence de philosophie change tout.
Dans l'approche producer-driven, c'est le producteur qui définit l'API. Il publie une spécification OpenAPI, et les consommateurs doivent s'y conformer. Avantage : simplicité et gouvernance centralisée. Inconvénient : le producteur ne sait pas vraiment ce que les consommateurs utilisent. Il peut faire un changement qui semble rétrocompatible mais qui casse un consommateur.
L'approche consumer-driven inverse cette dynamique. Chaque consommateur définit son propre contrat décrivant exactement ce dont il a besoin. Le producteur collecte tous ces contrats et prouve qu'il les respecte tous. Avantage : le producteur sait précisément ce qui est utilisé et détecte instantanément si un changement va casser quelqu'un. Inconvénient : plus de complexité.
Pact implémente l'approche consumer-driven. Chaque consommateur écrit des tests qui génèrent un fichier de contrat. Ce fichier est partagé avec le producteur, qui l'exécute contre son API réelle. Si le producteur satisfait tous les contrats, il peut déployer en confiance.
Comment Pact fonctionne : consumer, provider et broker
Comprendre le fonctionnement technique de Pact est essentiel pour l'utiliser correctement. Il y a trois composants principaux : consumer, provider, et broker.
Côté consumer, vous écrivez des tests Pact qui décrivent vos attentes. Pact crée un mock server qui simule les réponses. Vous écrivez "Quand je fais GET sur /users/123, j'attends un 200 avec userId, name et email". Pact enregistre cette interaction dans un fichier JSON appelé contrat.
Côté provider, vous prenez ce contrat et l'exécutez contre votre API réelle. Pact fait les requêtes HTTP décrites et vérifie que les réponses correspondent aux attentes. Si une réponse ne match pas, le test échoue.
Le Pact Broker est le serveur central où tous les contrats sont publiés et versionnés. Quand un consommateur modifie ses attentes, il publie un nouveau contrat. Quand un provider se prépare à déployer, il récupère tous les contrats et les vérifie. Le broker permet des déploiements découplés et vous alerte si vous tentez de déployer une version incompatible.
Ce que Pact sécurise (et ce qu'il ne sécurise pas)
Pact garantit que l'interface entre deux services est compatible. Si le consommateur attend un champ userId de type string et que le provider retourne un nombre, Pact détecte l'incompatibilité. Il garantit aussi que les changements du provider ne cassent pas les consommateurs existants. C'est un filet de sécurité contre les changements cassants involontaires.
Pact permet une documentation vivante des interactions entre services, et facilite le développement parallèle avec des mocks.
Mais Pact ne teste pas la logique métier. Il vérifie que les interfaces sont compatibles, pas que les données retournées sont correctes. Pact ne teste ni les performances, ni la sécurité, ni l'authentification. Il ne remplace pas les tests d'intégration end-to-end qui vérifient les flux complets. Et il ne garantit pas que le système fonctionne en production avec la charge réelle.
Comprendre ces limites évite de créer un faux sentiment de sécurité. Pact est un outil précieux pour un problème spécifique, mais ne résout pas tous les problèmes de qualité.
Les erreurs fréquentes d'implémentation
Passons aux erreurs courantes qui expliquent pourquoi beaucoup d'équipes abandonnent Pact.
Sur-spécifier les contrats : vérifier chaque champ de la réponse, même ceux non utilisés. Résultat : chaque changement du provider casse des contrats. Les contrats doivent décrire ce dont le consommateur a besoin, pas tout ce que le provider retourne.
Ne pas utiliser de matchers flexibles : Pact offre des matchers pour dire "ce champ est un string de type email" plutôt que "ce champ vaut exactement john@example.com". Sans matchers, vos contrats deviennent fragiles.
Tester la logique métier dans les contrats : les contrats vérifient la structure, pas les calculs. Ne testez pas qu'une remise donne exactement le bon montant dans un contrat.
Ne pas gérer les états du provider : Pact permet de dire "Given user 123 exists". Sans gestion d'états, vos tests provider deviennent impossibles à exécuter de manière déterministe.
Ignorer le versioning des contrats : publier des contrats sans version crée le chaos quand plusieurs versions coexistent en production.
Quand Pact devient contre-productif
Soyons honnêtes : Pact n'est pas pour tout le monde.
Dans une petite équipe avec trois ou quatre services simples, Pact est probablement du over-engineering. Des tests d'intégration classiques suffisent. Le coût de setup et maintenance dépasse les bénéfices.
Quand les APIs changent rarement, Pact apporte peu de valeur. Si votre API est stable depuis deux ans, quelques tests bien placés font l'affaire.
Si votre organisation n'a pas la culture des tests automatisés, introduire Pact sera un échec. Pact nécessite rigueur, discipline, et compréhension profonde des tests.
Pact devient aussi contre-productif quand les équipes transforment Pact en simple validation de schéma en écrivant tous les contrats côté provider. Et dans les environnements où les équipes refusent de collaborer, Pact empire les tensions.
Exemple réel : contrat valide mais production cassée
Laissez-moi partager un cas réel qui illustre les limites de Pact. Une équipe utilisait Pact entre leur service de commande (consumer) et leur service de catalogue (provider). Le contrat spécifiait qu'un GET sur /products/123 retournait un champ price de type number.
Tout fonctionnait en développement. Déploiement en production. Deux heures plus tard : les commandes ne se créaient plus.
Le service de catalogue avait ajouté un cache Redis. Sous forte charge, le cache retournait des prix légèrement arrondis différemment. Le service de commande comparait le prix reçu avec un prix calculé côté client. Avec les arrondis différents, cette validation échouait.
Le contrat vérifiait que price était un nombre. Techniquement respecté. Fonctionnellement, le système était cassé.
Cette histoire montre que Pact ne peut capturer que ce que vous lui dites explicitement. Les attentes implicites et les comportements sous charge échappent aux contrats. C'est pourquoi Pact doit être un outil parmi d'autres dans votre stratégie.
Bonnes pratiques d'adoption progressive
Si vous décidez d'adopter Pact, faites-le progressivement.
Commencez par un seul couple consumer-provider avec une API simple. Choisissez une API stable avec des équipes motivées. Implémentez, apprenez, faites des erreurs dans un contexte contrôlé.
Investissez dans la formation. Organisez des workshops pratiques. La documentation est excellente, mais rien ne remplace la pratique guidée.
Mettez en place votre Pact Broker dès le début. C'est le cœur du système. Sans broker, pas de gestion de versions, pas de visibilité sur les compatibilités.
Définissez des conventions d'équipe claires sur l'écriture des contrats. Quels matchers utiliser, comment nommer les états, comment versionner.
Intégrez Pact dans vos pipelines CI/CD dès le début. Les tests consumer à chaque commit du consumer, les tests provider à chaque commit et avant chaque déploiement du provider. Automatisez tout.
Mesurez et communiquez les bénéfices. Comptez les bugs détectés avant production, le temps gagné, la confiance accrue.
Soyez patients. L'adoption prend des mois, pas des semaines. Les premières semaines seront frustrantes. C'est normal.
Un outil puissant qui demande du discernement
Pact est un outil remarquablement puissant pour garantir la compatibilité des interfaces entre services dans une architecture distribuée. Quand il est bien compris et correctement implémenté, il transforme la façon dont les équipes collaborent.
Mais ce n'est pas une solution magique. Il ne remplace pas les autres tests, ne garantit pas que votre système fonctionne, et peut devenir contre-productif s'il est mal utilisé. Le succès avec Pact dépend de la compréhension de ses limites autant que de ses capacités.
Si votre contexte s'y prête et que vous êtes prêts à investir le temps nécessaire, Pact change vraiment la donne. Sinon, d'autres approches plus simples feront l'affaire.
FAQ
1) Pact, c’est pareil que la validation OpenAPI ?
Non. OpenAPI valide une structure d’interface. Pact valide la compatibilité d’interactions consommées. Les deux sont complémentaires.
2) Pact fonctionne-t-il pour les API GraphQL ?
Oui, Pact supporte GraphQL nativement. Vous définissez vos contrats avec les requêtes GraphQL que vous envoyez et les réponses que vous attendez. Le fonctionnement est le même que pour REST, avec les spécificités de GraphQL gérées automatiquement.
3) Faut-il un contrat par endpoint ou un contrat global ?
Un fichier de contrat contient toutes les interactions entre un consumer et un provider spécifiques. Donc un seul contrat par paire consumer-provider, même si ce contrat contient des dizaines d'interactions différentes. Ne créez pas un contrat par endpoint.
4) Combien coûte le Pact Broker ?
Il existe deux versions : le Pact Broker open source gratuit que vous hébergez vous-même, et Pactflow qui est la version SaaS managée par les créateurs de Pact. Pactflow est payant mais élimine toute la complexité d'infrastructure. Pour démarrer, l'open source suffit largement.
5) Pact remplace-t-il les tests d'intégration ?
Non. Pact teste les interactions isolées entre deux services avec des données mockées. Les tests d'intégration testent des flux complets avec les vraies dépendances. Vous avez besoin des deux : Pact pour la rapidité et la précision, intégration pour la validation end-to-end.