Une fois les contrats en place, les modifications de schéma demandent davantage de réflexion. Il n’est plus possible de simplement renommer une colonne et d’exécuter dbt build — le contrat détecte la discordance et le build échoue. Le système de versionnage des modèles dbt fournit le mécanisme permettant de faire évoluer des schémas sous contrat sans rompre les dépendances en aval.
Changements breaking vs non-breaking
Le sélecteur state:modified de dbt détecte cinq catégories de changements breaking lors de la comparaison avec un état antérieur du projet :
- Suppression d’une colonne
- Modification du type de données d’une colonne
- Suppression ou modification de contraintes
- Suppression totale de l’application du contrat
- Suppression ou renommage d’un modèle sous contrat
Les changements non-breaking passent sans générer d’erreur. Ajouter une nouvelle colonne, corriger un calcul sans changer les types, ou ajouter de nouvelles contraintes fonctionnent tous. Il suffit de mettre à jour le YAML et le SQL, et le contrat reste valide.
Cette distinction est importante pour le CI/CD. Les changements breaking nécessitent un versionnage. Les changements non-breaking nécessitent simplement une mise à jour du YAML du contrat parallèlement à la modification SQL.
Fonctionnement des versions
Pour les changements breaking, dbt attend la création d’une nouvelle version du modèle. Les versions utilisent de simples entiers, et chaque version se matérialise sous forme de table distincte (mrt__analytics__customers_v1, mrt__analytics__customers_v2) :
models: - name: mrt__analytics__customers latest_version: 2 config: contract: {enforced: true} columns: - name: customer__id data_type: int - name: customer__name data_type: string - name: customer__email_address data_type: string versions: - v: 1 deprecation_date: "2025-12-31" columns: - include: all exclude: [customer__email_address] - name: customer__email data_type: string - v: 2La version 2 utilise les définitions de colonnes de niveau supérieur (avec customer__email_address). La version 1 inclut toutes les colonnes sauf customer__email_address et ajoute l’ancienne colonne customer__email. Les deux versions se matérialisent sous forme de tables distinctes, toutes deux avec leurs contrats respectifs appliqués.
Un ref('mrt__analytics__customers') non épinglé se résout vers latest_version. Les consommateurs qui ont besoin de l’ancien schéma peuvent épingler avec ref('mrt__analytics__customers', v=1). Les anciennes versions reçoivent une deprecation_date, et dbt émet des avertissements lorsqu’une référence pointe vers une version dépréciée. Cela laisse aux consommateurs en aval le temps de migrer.
La fenêtre de migration
Le pattern de versionnage crée une fenêtre de migration :
- Définir la v2 avec le nouveau schéma aux côtés de la v1
- Définir
deprecation_datesur la v1 pour communiquer le calendrier - Déployer les deux versions — elles se matérialisent sous forme de tables distinctes
- Notifier les consommateurs — dbt génère des avertissements de dépréciation dans la sortie de build
- Supprimer la v1 une fois la date de dépréciation passée et tous les consommateurs migrés
Dans une configuration dbt Mesh, cela devient particulièrement précieux. Lorsqu’une autre équipe utilise ref('votre_projet', 'mrt__analytics__customers'), le versionnage lui permet de faire évoluer votre schéma sans casser ses builds. La référence cross-projet se résout vers latest_version, mais elle peut épingler une version spécifique si elle a besoin de temps pour s’adapter.
Les points de friction
Un vrai point de friction : la détection des changements breaking par dbt est jugée trop agressive par de nombreux utilisateurs (issue GitHub #8028). Elle déclenche des erreurs même lorsque le YAML du contrat a été intentionnellement mis à jour pour refléter un changement souhaité, nécessitant le versionnage même pour de simples corrections de précision. On se retrouve parfois à versionner pour des changements qui ressemblent davantage à des corrections qu’à des changements breaking.
Par exemple, passer de numeric à numeric(38,2) pour corriger un arrondi silencieux est techniquement un changement de type. Le sélecteur state:modified le signale comme breaking, nécessitant une nouvelle version — même si le changement est correctif et que les consommateurs en bénéficieraient immédiatement. L’intention derrière cette rigueur est saine (tout changement de type pourrait casser un consommateur), mais l’absence d’une échappatoire de type « c’est intentionnel, pas de versionnage » génère des frais généraux pour la maintenance courante.
La communauté a proposé plusieurs solutions : un flag --allow-breaking pour le CI, une annotation breaking: false dans le YAML, et une détection plus granulaire distinguant les changements élargissants (sûrs) des changements rétrécissants (dangereux). Début 2026, aucune n’a été fusionnée.
Gestion des changements de schéma pour les modèles incrémentaux
Pour les modèles incrémentaux avec contrats, la configuration on_schema_change est déterminante :
append_new_columns— Ajoute les nouvelles colonnes à la table existante. Compatible avec les contrats car il ne fait qu’élargir le schéma.fail— Fait échouer le build si le schéma a changé. L’option la plus sûre lorsque les contrats sont appliqués.sync_all_columns— À éviter. Supprime les colonnes absentes de la dernière exécution, ce qui crée exactement le type de changement breaking que les contrats sont censés prévenir. Une exécution incrémentale qui supprime une colonne violera le contrat lors du prochain refresh complet.ignore— Ignore silencieusement les changements de schéma. Risqué avec les contrats car cela permet au schéma réel de la table de diverger du schéma contractualisé au fil du temps.
La combinaison recommandée pour les modèles incrémentaux sous contrat est on_schema_change: 'fail' en fonctionnement normal, avec des migrations de schéma délibérées gérées par le versionnage lorsque des modifications sont nécessaires.
Quand versionner
Tout changement de schéma ne nécessite pas un versionnage. Le cadre de décision :
Versionner lorsque :
- Suppression d’une colonne dont dépendent les consommateurs
- Modification du type d’une colonne d’une manière pouvant casser les consommateurs (ex. :
integerenstring) - Renommage d’une colonne (c’est une suppression + ajout du point de vue du consommateur)
- Changement de sémantique même si le type est identique (ex. :
amountpasse de centimes à euros)
Ne pas versionner lorsque :
- Ajout d’une nouvelle colonne (non-breaking par définition)
- Ajout ou renforcement de contraintes (rend le contrat plus strict, ne casse pas les consommateurs)
- Correction d’un calcul ne modifiant pas le type de sortie
- Mise à jour des descriptions ou des métadonnées
L’objectif est de versionner uniquement lorsque le changement pourrait provoquer une défaillance en aval. Si la requête d’un consommateur pourrait échouer, versionnez. S’il obtiendrait simplement une colonne supplémentaire qu’il n’utilise pas, ne versionnez pas.