Contrôle de version des pipelines de données : code et données, main dans la main

Dans un écosystème de données, la gestion de versions s'impose pour suivre à la fois l'évolution du code et celle des données.

Introduction

Depuis sa création, Octo vit au rythme du contrôle de version (VC). Nous avons vu ces technologies propulser les plus grands projets logiciels et compris leur pouvoir pour transformer notre rapport aux données. Très tôt, nous avons conçu la « Revisioning Database », un cadre qui transpose les principes du versioning aux bases de données graphe de la plateforme Octo Gotham. Depuis, nous poussons ces technologies de VC toujours plus loin, au fil des terrains variés et changeants des écosystèmes de données modernes.

Au fond, le contrôle de version réunit des outils et des pratiques qui rendent possible la collaboration massive en développement logiciel. Il trace toutes les modifications (les « diffs ») apportées par chaque développeur, puis décide comment les intégrer dans une nouvelle version du logiciel. En prime, il consigne les métadonnées — qui a fait quoi, quand, avec quel message — et bien plus encore.

Transposé à un écosystème de données, le versioning sert à suivre les modifications du code (la logique qui nettoie, transforme, joint ou résout les données) et celles des données elles-mêmes. Comme pour le code, un système de VC pour les données enregistre les changements et les métadonnées associées. Capter les métadonnées compte souvent plus encore que capter les changements de valeurs : comme nous l'expliquions dans notre billet sur l'Ontology, les données n'ont pas de sens intrinsèque, et la métadonnée leur en confère. Elle précise la source d'origine, les politiques de sécurité appliquées, et les développeurs intervenus. À mesure que l'on bâtit des pipelines, ces métadonnées façonnent l'expérience des utilisateurs finaux : deux personnes, deux rôles, un même fichier — mais des vues différentes selon leurs autorisations.

Avec le lancement de Octo Foundry en 2016, nous avons mis le paquet sur des outils pour construire des pipelines de données robustes. La VC en est une brique essentielle, tant les interactions entre code et données sont complexes. Au fil de la vie d'un pipeline, la logique qui définit les transformations et produit jeux de données intermédiaires et finaux évolue. Savoir suivre cette évolution — et relier chaque résultat à la bonne combinaison de code et de données — devient vital.

Dans ce billet, nous clarifions ce qui définit le contrôle de version d'un pipeline de données, pourquoi il compte, et comment cadrer des exigences pour en tirer le meilleur.

Qu'est-ce que le contrôle de version d'un pipeline de données ?

Un pipeline se lit comme une chaîne de jeux de données reliés par des transformations logiques incrémentales. Un jeu de données, c'est un ensemble de fichiers (tableurs, documents, images, etc.). Les transformations, c'est le code qui les modifie pour en tirer de la valeur : nettoyer, joindre, résoudre, fusionner… Un pipeline peut accumuler des centaines d'étapes, chacune appliquant des changements à l'état précédent. Le contrôle de version dompte ce foisonnement de diffs — quoi, quand, par qui — dans un système où la logique se mêle intimement aux données.

Pour le code, les systèmes de VC orchestrent l'écriture et la gestion des changements en environnement partagé. La clé : le branchement. Chacun crée sa « branche » pour écrire, tester, corriger sans toucher à la branche principale. Une fois validée, la branche fusionne en production et devient une nouvelle version. Cette mécanique permet à des milliers de développeurs de cohabiter sur un même codebase, fonde le « DevOps » et autorise, si besoin, à « remonter le temps » vers une version antérieure.

Pour les données, le VC suit les modifications des contenus eux-mêmes. Différence majeure : les données servent souvent plusieurs finalités, là où le code vise en général une mission unique. Un même jeu peut être opérationnel ici, analytique là, informationnel ailleurs — avec des politiques et contraintes propres à chaque usage. Ces finalités créent des défis spécifiques. Si les données connaissent, elles aussi, branches et versions, l'essentiel réside dans leur provenance et leur destination. Comprendre d'où elles viennent, c'est la lignée (lineage) : elle révèle les politiques appliquées avant transformation. Comprendre où elles vont, c'est l'héritage (inheritance) : il détermine quelles politiques suivent les données tout au long du pipeline.

Autre particularité : les données prennent des formes multiples. Le code, lui, est du texte, régi par des règles strictes. Les autres données n'obéissent pas à ces contraintes. Pour tracer toutes les modifications, les systèmes de VC pour les données exigent souvent la conformité ACID (atomicité, cohérence, isolation, durabilité) : chaque changement est une unité indépendante. Facile pour du texte, beaucoup moins dès qu'on touche à l'image ou à l'audio. Pourtant, ces garanties ACID s'imposent pour versionner les données — et donc les pipelines. Réussir le versioning de pipelines suppose d'embrasser tout cela à la fois : versions, branches, lignée, héritage, garanties ACID, etc.

Au fur et à mesure que les pipelines évoluent, suivre les versions passées, présentes et parallèles donne aux organisations la main sur leurs données et des décisions plus sûres.

Exigences

  • La solution intègre un système de contrôle de version pour les pipelines qui suit, conjointement, la lignée des données et de la logique. Ce système rend superflus des environnements séparés de test, staging et production pour l'édition de données. Entretenir des environnements distincts alourdit performances, hébergement et administration. Quand le dev diffère de la prod, on code et on teste sur des données rarement identiques à celles de la prod réelle. Un VC solide permet de construire et tester la logique sur les données de production, avec la même configuration — puisque c'est le même environnement.
  • La solution prend en charge le branchement des données et de la logique. Les utilisateurs peuvent forker la base de production pour développer et tester de nouvelles capacités, chacun dans son sandbox. Une fois les transformations prêtes et validées, on les fusionne dans la base de code de production via un processus de revue défini.
  • La solution maintient une correspondance de journal persistante entre la version du code du pipeline et les versions du jeu de données de sortie. Dans un écosystème où données et code s'entrelacent, il faut un suivi fin. Cette correspondance indique quelle version de quel code a produit quel jeu, et autorise à mettre à jour, séparément et à la demande, code et données. Les deux avancent selon des versions indépendantes : des workflows isolés, mais des liens de traçabilité intacts.
  • La solution enregistre les métadonnées transactionnelles de chaque transformation : jeux d'entrée, jeux de sortie, source de la logique. Ces métadonnées sont le tissu conjonctif entre données et logique, et ouvrent la voie à des familles entières de processus et de fonctionnalités. Ensemble, ces entrées/sorties informationnelles et ce code de transaction composent une spécification de tâche — une recette unique pour identifier et matérialiser le jeu. Recette entièrement enregistrée, versionnée, branchée : on définit des politiques de rétention en aval et l'on peut revenir à toute version antérieure du pipeline.
  • La solution maintient un graphe de dépendances complet du pipeline. Cartographier les dépendances entre jeux de données garde l'ensemble cohérent. Ce graphe permet de : repérer et reconstruire les jeux dépassés par rapport à leurs entrées ; détecter en amont (« look ahead ») les erreurs, notamment de schéma ; servir d'ossature à des contrôles de santé des données ; et offrir des visualisations de lignée. Il structure aussi la propagation des contrôles de sécurité et d'accès tout au long du pipeline.
  • La solution garantit des transactions sérialisées et conformes ACID. Des transactions séquentielles et non chevauchantes produisent un historique clair des versions pour chaque jeu. On peut revoir et restaurer des états antérieurs. Et la conformité ACID écarte les cas limites de lecture/écriture qui saperaient la confiance.
  • La solution fournit des mécanismes pour regrouper et suivre les jeux de données, y compris la définition de données sources complétées par un ensemble de transformations. Un jeu de données, c'est la somme des changements opérés sur ses parents en amont. Chaque jeu représente une étape de la chaîne d'héritage : de multiples parents peuvent converger vers une sortie unique. Connaître cet héritage permet une propagation plus intuitive et granulaire des propriétés et permissions.
  • La solution assure la propagation automatique des politiques de données, des contrôles d'accès et des autres restrictions, d'un bout à l'autre du pipeline. Les politiques persistent à travers versions, branches et applications en aval. Vu le nombre d'utilisateurs et la diversité des rôles, un contrôle d'accès robuste s'impose. Au niveau le plus fin — ligne/colonne — il maximise la découverte et la collaboration sans sacrifier la gouvernance.

Conclusion

À chaque étape de cette série RFx sur les piliers d'un écosystème de données, un fil rouge s'impose : la confiance. Pour être efficaces, ces écosystèmes doivent inspirer confiance en leurs performances, leur fiabilité et leur sécurité. Le contrôle de version simultané des données et du code en est un fondement : il libère la créativité et l'expérimentation, sans risque. Les meilleurs systèmes traitent les données comme les développeurs traitent le code : on crée des branches pour développer et tester, à l'abri, dans un environnement sandboxé. Ces idées existent depuis longtemps pour le code ; leur application conjointe aux données et au code dans les écosystèmes de données émerge tout juste — et ouvre une voie enthousiasmante pour l'impact et la collaboration.