SYSTEM_INTEGRITY: %
Illustration abstraite d’un pipeline de données reliant contrats versionnés, flux CDC et contrôles de compatibilité
Données et bases de données ·

CDC et contrats de données : détecter une rupture de schéma avant qu’elle n’atteigne les consommateurs

Équipe Daymain 15 min

Points clés

  • Un contrat de données décrit plus qu’une structure : il formalise aussi le sens, la qualité, la propriété et la version d’un jeu de données.
  • Le CDC expose les changements de lignes et, selon les connecteurs, des changements de schéma ; ces signaux doivent rester accessibles pour diagnostiquer une rupture.
  • Backward, forward et full ne donnent pas les mêmes garanties et imposent des ordres de déploiement différents.
  • Les tests de compatibilité, les tests producteurs-consommateurs et l’observation des flux doivent intervenir avant la fusion, avant le déploiement et pendant la migration.
  • Une évolution additive, une période de double lecture ou d’écriture, puis une dépréciation mesurée réduisent le risque sans figer le système. ”
Sommaire

Une colonne apparaît dans une table, un producteur publie un champ renommé, un consommateur reçoit encore les anciens messages : sur le papier, le changement paraît limité. En pratique, il peut interrompre une synchronisation, rendre un traitement silencieusement faux ou envoyer des données incomplètes jusqu’à ce que quelqu’un remarque l’écart.

Le problème n’est donc pas seulement de savoir si un schéma a changé. Il faut déterminer si le changement modifie un contrat, qui le détecte, quelles garanties s’appliquent aux consommateurs et dans quel ordre les équipes doivent évoluer. Une approche fiable combine trois barrières : un contrat de données versionné, un flux de Change Data Capture — ou CDC, c’est-à-dire la capture des modifications apportées aux données — et des contrôles automatisés exécutés avant la fusion puis avant le déploiement.

Le vrai risque n’est pas le changement, mais la surprise

Les systèmes de données évoluent en permanence. Une équipe ajoute un attribut pour répondre à un nouveau besoin métier. Une autre remplace un identifiant numérique par une valeur textuelle. Un pipeline de reporting commence à lire une table directement, alors que son propriétaire pensait n’exposer qu’une vue stable. Chacun de ces gestes peut être raisonnable localement. La rupture apparaît lorsque les hypothèses des autres équipes restent invisibles.

Il faut distinguer deux notions souvent mélangées. Une évolution de schéma concerne la forme déclarée d’une donnée : ses champs, leurs types, leur présence ou leur structure imbriquée. Un changement de contrat touche la promesse faite aux consommateurs. Il peut modifier le sens d’un champ, sa qualité attendue, sa fréquence de publication, son propriétaire ou la manière dont une valeur absente doit être interprétée. Ajouter une colonne facultative peut être une évolution compatible. Réutiliser une colonne existante pour lui donner un autre sens est un changement de contrat, même si le fichier de schéma reste valide.

Cette distinction explique pourquoi la simple validation syntaxique ne suffit pas. Un document peut respecter la grammaire attendue et pourtant introduire une ambiguïté fonctionnelle. À l’inverse, une modification techniquement visible peut rester sans effet pour les consommateurs si elle respecte les garanties convenues. La détection doit donc répondre à trois questions : qu’est-ce qui a changé, qui dépend de cette donnée et quelles versions peuvent coexister ?

Le risque augmente lorsque plusieurs mécanismes se superposent. Une base relationnelle change, un outil CDC transforme ses lignes en événements, un registre contrôle le schéma des messages, puis un sink les aplati avant de les écrire ailleurs. À chaque étape, une information peut être renommée, perdue ou interprétée différemment. La chaîne de contrôle doit suivre le même trajet que la donnée, sans confondre les responsabilités.

Le premier principe est donc simple : ne pas attendre qu’un consommateur échoue pour découvrir qu’il existait un contrat. Il faut rendre ce contrat explicite, comparable et testable.

Un contrat de données doit décrire une promesse, pas seulement des champs

Un contrat de données sert de point de référence commun entre ceux qui produisent, transforment et consomment un jeu de données. Le standard ODCS, présenté dans sa documentation comme un contrat sous forme de fichier YAML, permet de centraliser des éléments de structure, de sémantique, de qualité, de propriété et de version. Cette approche est utile parce qu’elle déplace la discussion : on ne compare plus seulement deux définitions techniques, on compare deux promesses publiées.

Un contrat suffisamment explicite répond notamment à ces questions :

  • Quelle est la structure exposée et quel type possède chaque champ ?
  • Que signifie chaque valeur, et quelle unité ou convention s’applique ?
  • Quelles règles de qualité sont attendues ?
  • Qui possède la donnée et qui peut approuver son évolution ?
  • Quelle version est consommée, et selon quelle durée l’ancienne version reste-t-elle disponible ?

La version n’est pas un simple numéro ajouté au nom d’un fichier. Elle doit permettre de comprendre la trajectoire du contrat. Une comparaison entre deux versions doit faire apparaître un champ ajouté, une contrainte renforcée, un type modifié ou une définition révisée. La documentation ODCS indique qu’il est possible de valider la structure du contrat, de comparer deux versions avec une commande de changelog et de tester sa conformité contre une source réelle. Ces opérations donnent une base concrète à la revue : l’équipe peut regarder ce qui change avant de discuter de la date de mise en production.

La conformité à une source réelle est particulièrement importante pour éviter un contrat théorique. Un schéma peut annoncer qu’un champ est toujours renseigné alors que les données produites contiennent des valeurs absentes. Il peut aussi déclarer une plage ou un format que les flux ne respectent pas régulièrement. Le contrôle ne remplace pas l’analyse métier, mais il révèle l’écart entre la promesse et le comportement observé.

Cette formalisation ne doit pas devenir une bureaucratie parallèle. Un bon contrat n’essaie pas de décrire chaque détail interne du producteur. Il décrit ce dont les consommateurs peuvent raisonnablement dépendre. C’est là que la notion de propriété devient pratique : quelqu’un doit pouvoir expliquer le sens d’un champ, accepter une évolution et organiser sa dépréciation.

Enfin, le contrat doit être exploitable par les outils. S’il reste dans une page difficile à comparer, il ne peut pas bloquer une fusion ni déclencher un test. Le fichier machine-readable devient alors un artefact de développement : il est versionné avec le code ou dans un dépôt de contrats, soumis à une revue et utilisé comme référence par la chaîne CI/CD.

Le CDC expose le changement, mais ne décide pas seul de sa gravité

Le Change Data Capture observe les modifications apportées à une source et les publie sous forme d’événements. Dans la documentation Debezium, ces événements décrivent notamment l’opération réalisée, les métadonnées de la source et l’état avant et après la modification. Pour une équipe qui cherche à comprendre une rupture, cette enveloppe est précieuse : elle permet de distinguer une création, une mise à jour ou une suppression, mais aussi de replacer l’événement dans son contexte d’origine.

Le CDC répond donc à une question différente de celle du contrat. Le contrat dit ce qui devrait être accepté. Le CDC montre ce qui s’est effectivement produit. L’un formalise une attente ; l’autre fournit des observations. Les réunir permet de repérer un écart, par exemple un champ qui devient absent dans les événements alors qu’il est déclaré requis, ou une structure qui évolue sans version de contrat correspondante.

Selon le connecteur utilisé, le flux peut également exposer des événements dédiés aux modifications de schéma. Ils ne doivent pas être traités comme de simples messages ordinaires. Leur rôle est de signaler qu’une structure, une table ou une relation de données a évolué. Ils peuvent alimenter une chaîne d’alerte, déclencher une comparaison avec la dernière version acceptée ou ouvrir une étape de validation avant que les consommateurs ne reçoivent la nouvelle forme.

Une précaution mérite une place centrale : ne pas perdre l’enveloppe trop tôt. L’aplatissement des événements CDC produit des messages plus simples pour certains consommateurs, mais il peut supprimer ou masquer une partie du contexte original. Le mode « état du nouvel enregistrement » peut convenir à un sink qui veut surtout obtenir la dernière valeur ; il est moins adapté à un diagnostic qui doit savoir quelle opération a eu lieu, quand et à partir de quel état.

Une architecture prudente conserve donc les événements complets dans le flux Kafka, puis applique l’aplatissement au niveau du sink lorsque ce choix est nécessaire. Les consommateurs finaux obtiennent une représentation adaptée à leur usage, tandis que l’équipe garde la matière nécessaire pour enquêter sur une rupture. Ce découplage évite de sacrifier l’observabilité pour simplifier une interface.

Le CDC ne garantit toutefois pas la compatibilité. Il peut parfaitement capturer et transporter un changement que certains consommateurs ne savent pas traiter. Il ne remplace pas un registre de schémas, un contrat versionné ni un test de consommation. Sa valeur apparaît lorsqu’il devient un signal relié à ces contrôles : changement détecté, contrat comparé, consommateurs connus identifiés, migration suivie.

Choisir une compatibilité, c’est choisir un ordre de migration

Dans un flux Kafka utilisant un registre de schémas, la politique de compatibilité définit les versions qui peuvent coexister. Ce choix n’est pas une option abstraite du registre : il indique implicitement quelle partie du système doit bouger en premier.

La compatibilité backward garantit qu’un nouveau schéma peut être lu avec les données produites par l’ancien schéma, selon les règles couvertes par la politique. Dans ce mode, la documentation Confluent indique que les consommateurs doivent être mis à niveau avant que les producteurs ne commencent à publier de nouveaux événements. Ils doivent donc savoir gérer l’ancienne forme et la nouvelle avant l’arrivée de cette dernière.

La compatibilité forward inverse cette logique : l’ancien consommateur doit pouvoir lire les nouveaux événements. Les producteurs sont alors mis à niveau avant les consommateurs. Cette stratégie peut être utile lorsqu’une équipe ne peut pas déployer tous ses lecteurs en même temps, à condition que la garantie corresponde bien au comportement réel des sérialiseurs et des applications.

La compatibilité full cherche à couvrir les deux directions. Producteurs et consommateurs peuvent évoluer de manière plus indépendante, sous réserve des garanties effectivement couvertes par la politique. La version transitive de ces modes étend la comparaison à plusieurs versions antérieures, et pas seulement à la précédente. Cette différence compte dans un environnement où plusieurs versions restent actives pendant une migration.

Il n’existe pas de politique universellement meilleure. Le choix dépend de la topologie du flux, de la durée de coexistence des versions et de la capacité à déployer les consommateurs. Une équipe qui active une politique stricte sans adapter son processus peut bloquer des évolutions légitimes. Une équipe qui autorise tout peut conserver un registre cohérent tout en cassant une application qui dépend d’une convention non exprimée.

Dans la pratique, les changements additifs sont un point de départ plus sûr : ajouter un champ optionnel, préserver les champs existants et ne pas réinterpréter une valeur déjà consommée. Mais « additif » ne signifie pas automatiquement « sans risque ». Un champ déclaré optionnel peut devenir indispensable dans la logique d’un nouveau consommateur. Une valeur par défaut peut masquer un défaut de production. Le contrat doit donc préciser ce que l’absence signifie et les tests doivent couvrir les usages réels.

Une règle de décision utile consiste à écrire l’ordre attendu avant de modifier le schéma. Si les consommateurs doivent être déployés en premier, le pipeline doit vérifier qu’ils savent lire les deux versions. Si le producteur passe d’abord, il faut démontrer que les lecteurs existants tolèrent la nouvelle forme. Le registre fournit une barrière, mais la séquence opérationnelle transforme cette barrière en protection réelle.

Les contrôles doivent bloquer avant la fusion, pas seulement alerter après coup

Une chaîne de détection efficace commence dans le dépôt. Lorsqu’un contrat ou un schéma change, le pipeline CI/CD compare la nouvelle version à une référence connue. Il valide la structure du contrat, produit un changelog lisible et exécute les règles de compatibilité choisies. Si le changement est incompatible, la fusion doit être bloquée ou soumise à une approbation explicitement documentée.

Cette première barrière contrôle la forme et les garanties déclarées. Elle doit être complétée par des données de test représentatives, voire par une vérification de conformité contre une source réelle lorsque le contrat le permet. Le but n’est pas de prétendre couvrir tous les cas, mais de détecter les écarts les plus coûteux avant qu’ils n’atteignent le registre ou le flux.

La deuxième barrière concerne les interactions réellement consommées. Les tests de contrats pilotés par les consommateurs, comme ceux documentés par Pact, ne se limitent pas à demander si deux schémas sont identiques. Ils vérifient les échanges et les champs effectivement utilisés par un consommateur. Cette nuance évite de traiter tous les lecteurs comme s’ils dépendaient de toute la surface du message.

Un test de compatibilité de schéma peut accepter une évolution qui reste problématique pour une application. Inversement, un test de contrat consommateur peut signaler qu’un champ apparemment secondaire est indispensable à une interaction précise. Les deux contrôles ne se remplacent pas. Le premier protège une interface déclarée ; le second protège un usage observé.

Pour des schémas Protobuf, un outil comme Buf peut comparer une version courante à une référence antérieure et détecter des changements incompatibles selon les règles configurées. L’intérêt n’est pas l’outil en lui-même, mais le principe : rendre la comparaison déterministe et l’exécuter au moment où le changement est encore peu coûteux à corriger.

Le pipeline devrait produire un résultat compréhensible par une équipe humaine. « Échec de compatibilité » est trop vague. La revue doit voir quel champ a été supprimé, quel type a changé, quelle contrainte a été renforcée et quel consommateur est concerné, lorsque cette information est disponible. La décision peut alors être technique et métier : corriger le changement, publier une nouvelle version ou accepter une rupture planifiée avec un plan de migration.

Enfin, le contrôle doit exister à deux moments. Avant la fusion, il protège le dépôt et évite de normaliser un schéma incompatible. Avant le déploiement, il protège l’environnement cible contre une divergence entre la version testée et la version réellement publiée. Ces barrières ne garantissent pas l’absence d’incident, mais elles déplacent la découverte vers un moment où l’équipe peut encore choisir.

Déployer sans rupture : coexistence, observation et retrait

Une migration sûre commence par une modification qui peut coexister avec l’ancienne version. Le cas le plus lisible est l’ajout d’un champ optionnel dont le sens, le format et la valeur en l’absence sont documentés. Le producteur peut ensuite commencer à le renseigner, tandis que les consommateurs existants continuent de fonctionner. Cette étape additive ne dispense pas des tests : elle crée seulement une zone de transition.

Lorsque l’évolution est plus profonde, la double écriture ou la double lecture devient un mécanisme de migration. En double écriture, le producteur publie temporairement l’ancien et le nouveau format. En double lecture, le consommateur sait lire les deux représentations et compare éventuellement leurs résultats. Ces techniques ajoutent de la complexité ; elles ne doivent pas être introduites par réflexe. Elles sont pertinentes lorsque le coût d’une rupture dépasse le coût d’une période de coexistence contrôlée.

La séquence dépend de la compatibilité retenue. En backward, on commence par rendre les consommateurs capables de lire la nouvelle version, puis on active la production des nouveaux événements. En forward, on met d’abord à niveau le producteur afin que les anciens consommateurs puissent continuer à lire les événements. En full, l’indépendance est plus grande, mais elle reste conditionnée par les règles définies et par les consommateurs qui échappent aux inventaires officiels.

Le CDC aide à observer cette transition. Les métriques peuvent suivre la présence des anciennes et nouvelles formes, les erreurs de désérialisation, les messages rejetés et les événements de changement de schéma. Les événements complets conservent le contexte nécessaire pour relier une anomalie à une opération et à sa source. Une alerte utile ne dit pas seulement qu’un flux a changé ; elle indique quelle version a été publiée, quel contrat était attendu et quelle population de consommateurs reste active.

La dépréciation doit être traitée comme une étape, pas comme une intention. Avant de retirer l’ancienne version, l’équipe vérifie que les consommateurs connus ont migré et qu’aucune lecture active ne dépend encore de la forme précédente. Les outils ne voient pas toujours les usages indirects ou les accès non déclarés. La décision doit donc croiser les signaux du registre, les tests, les métriques du pipeline et la connaissance des équipes.

Le retrait peut ensuite être progressif : réduire la production de l’ancienne forme, maintenir une période d’observation, puis supprimer la compatibilité seulement lorsque la coexistence n’est plus nécessaire. Cette discipline évite le scénario classique où une ancienne version reste officiellement supportée sans limite, jusqu’à rendre toute évolution pénible.

Une méthode opératoire pour relier contrat, CDC et CI/CD

La méthode tient moins à un outil unique qu’à la continuité entre les étapes. Elle peut servir de fil conducteur à une équipe qui doit modifier un schéma sans connaître précisément tous ses consommateurs.

  1. Décrire la promesse. Créer ou mettre à jour le contrat machine-readable : structure, sens, qualité, propriétaire, version et règles d’absence ou de valeur par défaut. Ne pas se contenter du nom des champs.
  2. Comparer avec l’existant. Produire le changelog entre la version proposée et la référence précédente. Classer chaque modification : additive, comportementale, incompatible ou simplement documentaire.
  3. Choisir la garantie. Définir backward, forward, full ou une variante transitive selon l’ordre de déploiement possible et la durée de coexistence nécessaire. Écrire cet ordre dans le plan de migration.
  4. Tester les usages. Exécuter les contrôles de compatibilité du schéma, puis les tests de contrats producteurs-consommateurs sur les interactions réellement utilisées. Ajouter une conformité à des données réelles lorsque la source et les règles le permettent.
  5. Publier de façon compatible. Favoriser un changement additif. Si ce n’est pas possible, prévoir une double lecture ou une double écriture, avec une durée de coexistence et un responsable clairement identifié.
  6. Observer le flux CDC. Conserver les événements complets lorsque le diagnostic et l’historique comptent. Relier les événements de changement de schéma, les erreurs de consommation et les métriques de conformité à la version du contrat.
  7. Retirer avec preuve. Vérifier l’absence de consommateurs actifs de l’ancienne version, réduire progressivement son usage et ne supprimer la compatibilité qu’après la période d’observation prévue.

Cette procédure révèle aussi les angles morts. Si personne ne sait qui consomme une donnée, le problème n’est pas seulement l’absence d’un test : c’est une faiblesse de gouvernance et d’inventaire. Si les consommateurs sont connus mais ne peuvent pas être testés, l’interface est peut-être trop implicite. Si le CDC contient le contexte mais que le sink l’écrase, la chaîne de diagnostic est incomplète.

Il est tentant de multiplier les contrôles jusqu’à rendre chaque changement pénible. Ce serait une mauvaise réponse. La bonne question est plutôt : quelle garantie est nécessaire pour ce flux précis, et quelle preuve peut être produite au coût le plus raisonnable ? Un flux critique et partagé mérite une politique stricte, des tests de consommateurs et une observation détaillée. Un flux interne à un seul service peut adopter un processus plus léger, à condition que son périmètre soit réellement maîtrisé.

Le contrat, le CDC et la CI/CD forment ainsi trois niveaux différents. Le contrat nomme l’interface. Le CDC expose les faits. La CI/CD transforme les règles en décision avant livraison. La robustesse vient de leur articulation, pas de la sophistication isolée de l’un d’eux.

Conclusion

Un changement de schéma devient dangereux lorsqu’il arrive sans contexte, sans version et sans ordre de migration. La détection fiable commence par un contrat de données qui décrit la structure mais aussi le sens, la qualité, la propriété et la durée de vie d’une version. Elle se poursuit avec un CDC capable de montrer les modifications réellement produites, sans perdre trop tôt l’enveloppe qui permet de les comprendre.

Les politiques backward, forward et full donnent ensuite un cadre à la coexistence, mais elles ne choisissent pas la stratégie à la place des équipes. Elles indiquent quelles versions peuvent se lire et, par conséquent, qui doit être déployé en premier. Les tests de compatibilité et les tests centrés sur les interactions des consommateurs complètent cette protection avant la fusion et avant le déploiement.

La méthode la plus pragmatique reste progressive : versionner, comparer, tester, publier un changement additif lorsque c’est possible, maintenir temporairement deux formes si nécessaire, observer les événements CDC et retirer l’ancienne version seulement après vérification. Ce n’est pas une promesse d’absence totale d’incident. C’est mieux : une manière de rendre les ruptures plus visibles, les décisions plus réversibles et les migrations moins dépendantes d’une découverte en production.

Sources

  1. Schema Evolution and Compatibility Types for Confluent Platform — Confluent
  2. Debezium Documentation — Debezium
  3. New Record State Extraction — Debezium
  4. Open Data Contract Standard — Data Contract CLI
  5. Introduction — Pact
  6. Detecting breaking changes — Buf

Équipe Daymain

Partager