Adopter les contrats de modèles dbt dans un projet existant nécessite une approche par phases pour éviter de casser tous les modèles de production en même temps. Les étapes ci-dessous s’appliquent aux projets de tailles variées.
Étape 1 : identifier les modèles qui ont besoin de contrats
Tous les modèles n’ont pas besoin d’un contrat. Les meilleurs candidats sont les modèles mart qui servent des consommateurs en aval, en particulier ceux marqués access: public. L’application des contrats sur les modèles base ou intermédiaires ajoute une charge sans bénéfice significatif — ces couches sont internes à votre DAG, et leurs consommateurs sont vos propres modèles, pas des équipes externes ou des outils BI.
Le package dbt-project-evaluator possède une règle appelée fct_public_models_without_contract qui identifie les modèles publics manquant de contrats. Cela vous donne une liste priorisée des modèles dont d’autres projets ou outils dépendent mais qui manquent de garanties de schéma.
Une priorisation pratique :
- Modèles mart publics consommés par des outils BI ou reverse ETL
- Modèles référencés entre projets dans une configuration dbt Mesh
- Modèles avec un historique de changements cassants ayant causé des incidents en aval
- Modèles à haute visibilité alimentant des tableaux de bord exécutifs ou des partenaires externes
Étape 2 : générer le YAML initial
Pour les modèles existants, dbt-codegen génère des définitions prêtes pour les contrats :
dbt run-operation generate_model_yaml \ --args '{"model_names": ["mrt__analytics__customers"], "include_data_types": true}'Examinez attentivement le résultat. Trois problèmes courants dans le YAML généré :
-
Types numériques sans précision.
dbt-codegenpeut produirenumericquand la colonne réelle utilisenumeric(38,2).numericnu adopte par défaut scale=0 sur certaines plateformes, ce qui ne stocke que des nombres entiers. Spécifiez toujours la précision et l’échelle explicitement pour les colonnes décimales. -
Alias de types qui ne correspondent pas. Le type généré peut être
STRINGmais votre SQL produitVARCHAR. dbt gère les alias, mais vérifiez que le type généré s’aligne avec ce que votre SQL retourne réellement. -
Colonnes manquantes. Si votre modèle utilise
SELECT *,dbt-codegencapture les colonnes actuelles, mais un changement de schéma source pourrait ajouter des colonnes qui ne figurent pas dans votre contrat. Envisagez de remplacerSELECT *par des listes de colonnes explicites avant d’activer les contrats.
Avant d’activer l’application, exécutez dbt compile pour faire remonter les incompatibilités de types sans affecter aucune table construite. L’étape compile effectue le preflight check sans exécuter de DDL.
Étape 3 : activer les contrats par phases
Phase 1 : commencer petit
Activez les contrats sur quelques modèles mart en premier. Commencez avec les contraintes not_null (la seule contrainte appliquée sur toutes les plateformes majeures) et ajoutez des contraintes informelles comme primary_key avec warn_unenforced: false. Associez les contraintes non appliquées à des tests dbt.
models: - name: mrt__analytics__customers config: contract: enforced: true columns: - name: customer__id data_type: integer constraints: - type: not_null - type: primary_key warn_unenforced: false data_tests: - unique - not_null - name: customer__name data_type: text - name: customer__lifetime_value data_type: numeric(38,2)Exécutez les modèles contractualisés dans votre environnement de développement. Corrigez les incompatibilités de types que le preflight check détecte. Déployez en production uniquement quand les builds sont propres.
Phase 2 : étendre à d’autres marts
Une fois le premier lot stable, étendez à d’autres modèles mart. Utilisez la configuration au niveau des répertoires pour activer les contrats sur des dossiers mart entiers :
models: my_project: marts: +contract: enforced: trueCette approche vous force à ajouter du YAML de contrat pour chaque modèle mart, ce qui fait remonter les modèles qui manquent de définitions de colonnes.
Phase 3 : franchir les frontières inter-équipes
Étendez les contrats aux modèles intermédiaires qui servent de frontières d’API inter-équipes. Ce sont des modèles où une équipe produit des données qu’une autre équipe consomme. Même au sein du même projet dbt, l’application des contrats sur ces frontières empêche le refactoring d’une équipe de casser silencieusement les modèles en aval d’une autre équipe.
Étape 4 : intégrer avec la CI/CD
La CI/CD est là où les contrats apportent le plus de valeur. Les changements cassants remontent dans les pull requests avant que quiconque ne fusionne quoi que ce soit.
Dans dbt Cloud, les jobs CI utilisent state:modified+ pour comparer le manifest actuel avec le dernier run de production réussi. Les changements cassants apparaissent directement dans les commentaires de pull request. Le système de versionnage gère l’évolution légitime des schémas, tandis que les contrats détectent les changements non intentionnels.
Pour les utilisateurs de dbt Core, le flag --empty (disponible depuis la version 1.8) active des vérifications CI de gouvernance uniquement :
dbt build --select state:modified+ --empty --defer --state ./prod-manifestCela construit les structures de tables et valide les contrats sans traiter aucune donnée. La validation du schéma s’exécute à un coût de calcul quasi nul, ce qui la rend pratique pour vérifier chaque pull request.
Le flag --empty est significatif car il découple la validation du schéma du traitement des données. Sans lui, les jobs CI devaient soit s’exécuter sur des données réelles (coûteux, lent) soit ignorer complètement la validation des contrats. Avec --empty, on obtient une validation complète des contrats et des contraintes avec un coût warehouse minimal. Un job CI qui prendrait 20 minutes et scannerait des téraoctets se termine en quelques secondes.
Pièges courants
Activer les contrats sur trop de modèles à la fois. La génération initiale du YAML présente presque toujours des incompatibilités de types. Activez les contrats modèle par modèle pour corriger les problèmes de manière incrémentale plutôt que de se noyer dans un mur d’erreurs de compilation.
Oublier de mettre à jour les contrats lors de la modification du SQL. Un contrat est une modification sur deux fichiers : le SQL et le YAML. Si vous ajoutez une colonne à votre SQL mais oubliez de l’ajouter au YAML, le build échoue. Certaines équipes ajoutent une vérification CI qui avertit quand des fichiers SQL sont modifiés sans changements YAML correspondants.
Utiliser SELECT * dans les modèles contractualisés. Les contrats requièrent que chaque colonne soit déclarée. SELECT * signifie que tout changement de schéma en amont ajoute une colonne à votre modèle qui ne figure pas dans le contrat, cassant le build. Remplacez SELECT * par des listes de colonnes explicites dans tout modèle contractualisé.
Ignorer le flag warn_unenforced. Sans warn_unenforced: false sur les contraintes informelles, dbt génère des avertissements à chaque build. Avec le temps, les équipes cessent de lire les avertissements et les vrais problèmes sont noyés. Supprimez les avertissements pour les contraintes que vous avez délibérément choisi de gérer avec des tests.
Trajectoire d’adoption
Les premiers modèles contractualisés nécessitent le plus d’effort — apprendre le système de types, corriger le YAML et établir des patterns. Au vingtième modèle, générer et activer un contrat prend quelques minutes.
La valeur principale de l’intégration CI/CD est que chaque changement de schéma devient révisé et intentionnel. Les changements de types de colonnes qui cassaient auparavant silencieusement des tableaux de bord remontent dans les pull requests avant la fusion.