Les contrats de modèles dbt, introduits dans Core v1.5 (avril 2023), appliquent des garanties de schéma au moment du build. Quand vous activez contract: {enforced: true} sur un modèle, dbt ajoute une vérification preflight qui empêche le modèle de se matérialiser si sa sortie ne correspond pas à la déclaration YAML. Trois ans d’utilisation en production ont mis en évidence les forces et les limites de la fonctionnalité.
L’application en deux étapes
Quand un contrat est activé, dbt effectue deux choses lors du build.
Premièrement, une vérification preflight à la compilation. dbt compare les colonnes que votre requête SQL retourne avec ce que vous avez déclaré en YAML. Il vérifie les noms de colonnes et les types de données. En cas de divergence, dbt génère une erreur de compilation avant qu’aucune table ne soit créée. Le modèle ne se matérialise jamais.
Deuxièmement, l’inclusion de contraintes DDL. dbt inclut vos contraintes déclarées dans les instructions DDL envoyées à l’entrepôt. Au lieu d’un CREATE TABLE nu, le DDL spécifie les types de colonnes et les contraintes comme NOT NULL ou PRIMARY KEY. Que l’entrepôt applique effectivement ces contraintes est une question entièrement séparée.
La sortie d’erreur est suffisamment spécifique pour agir immédiatement :
Compilation Error in model mrt__analytics__customers (models/marts/analytics/mrt__analytics__customers.sql)This model has an enforced contract that failed.
| column_name | definition_type | contract_type | mismatch_reason || ------------- | --------------- | ------------- | ------------------ || customer__id | TEXT | INT | data type mismatch |Trois raisons de divergence peuvent apparaître : data type mismatch, missing in contract (la colonne existe dans le SQL mais pas dans le YAML), et missing in definition (la colonne est déclarée dans le YAML mais absente du SQL). Le tableau de diagnostic vous indique exactement quoi corriger.
Fail-fast vs test-après
Ce comportement fail-fast est ce qui distingue les contrats des tests dbt. Les tests s’exécutent après la construction d’un modèle, ce qui signifie que la table existe déjà et que les modèles en aval ont peut-être déjà la consommé. Les contrats empêchent le modèle de se matérialiser en premier lieu. L’état erroné n’atteint jamais l’entrepôt.
Mais les contrats ne valident que la forme. Ils vérifient que les colonnes existent et ont les bons types. Ils ne vérifient pas si les données dans ces colonnes ont du sens. Une colonne status pourrait contenir des valeurs jamais vues auparavant, et les contrats ne le détecteraient pas. Les tests de qualité des données restent indispensables pour la validation de contenu. Les contrats gèrent la structure, les tests gèrent le contenu, et vous avez besoin des deux.
Configuration
La configuration réside dans votre fichier de propriétés YAML :
models: - name: mrt__analytics__customers access: public config: materialized: table contract: enforced: true columns: - name: customer__id data_type: integer constraints: - type: not_null - name: customer__name data_type: text - name: customer__lifetime_value data_type: numeric(38,2) - name: customer__is_active data_type: booleanChaque colonne de votre modèle doit être listée avec un name et un data_type. Les contrats partiels ne sont pas pris en charge. Si votre SQL retourne une colonne qui n’est pas dans le YAML, ou si le YAML liste une colonne que le SQL ne retourne pas, le build échoue. C’est intentionnel : un contrat qui ne couvre pas tout n’est pas vraiment un contrat.
L’ordre des colonnes dans votre SQL n’a pas d’importance — la vérification preflight est indépendante de l’ordre. Mais dbt réordonne les colonnes de sortie pour correspondre à l’ordre des colonnes du YAML dans le DDL, ce qui compte si quoi que ce soit en aval dépend de la position des colonnes.
Vous pouvez aussi activer les contrats au niveau du répertoire dans dbt_project.yml, ce qui est l’approche la plus courante pour les équipes qui veulent des contrats sur tous les modèles mart :
models: my_project: marts: +contract: enforced: trueGestion des types
dbt gère l’alias de types entre plateformes (string correspond à text sur Postgres, par exemple), mais ne compare pas le dimensionnement granulaire. varchar(256) et varchar(257) sont traités comme équivalents.
La précision compte dans un cas spécifique : les types numeric nus peuvent prendre par défaut scale=0, qui stocke uniquement des entiers. Si votre modèle calcule des décimales, spécifiez toujours explicitement la précision et l’échelle (numeric(38,2)). dbt 1.7+ avertit quand les types numeric manquent de précision explicite, ce qui aide à détecter cela avant d’arrondir silencieusement vos revenus à des entiers.
Support des matérialisations
Les contrats fonctionnent avec les matérialisations table, incremental et view (les vues ont un support limité des contraintes). Ils ne fonctionnent pas sur les modèles éphémères, les vues matérialisées, les modèles Python, les sources, les seeds ou les snapshots.
Pour les modèles incrémentaux, définissez on_schema_change sur append_new_columns ou fail. Évitez sync_all_columns, qui supprime les colonnes non présentes dans le dernier run et crée exactement le type de changement cassant que les contrats sont censés prévenir.
La place des contrats
Les contrats constituent une couche d’une stratégie de qualité, pas la stratégie complète. Ils protègent la forme des modèles dans votre projet dbt. Ils ne valident pas les sources (bien que vous puissiez placer un modèle de base contractuel directement sur une source). Ils ne vérifient pas le contenu des données. Et ils n’empêchent pas les mauvaises données d’entrer dans votre entrepôt.
Les meilleurs candidats pour les contrats sont les modèles mart qui servent les consommateurs en aval, notamment ceux marqués access: public. Dans une configuration dbt Mesh, les contrats se combinent avec les contrôles d’accès et le versionnage des modèles pour former le socle de gouvernance des références cross-projets. Quand une autre équipe utilise ref('your_project', 'mrt__analytics__customers'), le contrat garantit qu’elle obtiendra les colonnes et les types qu’elle attend.
Pour la protection au niveau des sources, il faut des outils en dehors de dbt : les schema registries pour les flux d’événements, les contrats des outils EL (les contrats de schéma natifs de dlt sont particulièrement capables), ou la validation à l’exécution. L’écosystème d’outils plus large couvre ces points d’application.