Le pattern Observe & Fix, décrit par Baruch Jacob, intègre la remédiation de la qualité des données directement dans le DAG dbt. La couche base détecte les problèmes et les expose via des tests. Les couches intermédiaire et mart appliquent les corrections. La logique de remédiation est explicite, testable et versionnée — distincte des mécanismes de retry dans l’orchestrateur ou dans des outils externes.
L’idée centrale
Séparer la détection de la remédiation. Les modèles base et leurs tests détectent le problème. Les modèles en aval appliquent la correction. Cela maintient la responsabilité de chaque couche bien délimitée : les modèles base sont une représentation fidèle de la source (problèmes inclus), et les modèles en aval sont l’endroit où l’on décide quoi faire de ces problèmes.
L’alternative — corriger les problèmes de qualité dans le modèle base — obscurcit ce à quoi ressemble réellement la source. Si votre modèle base supprime silencieusement les enregistrements nuls, vous perdez la visibilité sur la fréquence à laquelle la source envoie des nulls. Si vous fusionnez des valeurs erronées dans la couche base, les modèles en aval ne peuvent plus distinguer « la source a envoyé une valeur valide » de « la source a envoyé des données corrompues que nous avons corrigées ». Le pattern observe-fix préserve cette distinction.
Techniques de remédiation courantes
Déduplication
Les systèmes sources envoient parfois des enregistrements en double. Les CRM avec un lag de réplication, les flux d’événements avec une livraison au moins une fois, les réponses API qui incluent des fenêtres temporelles chevauchantes. Plutôt que de dédupliquer dans le modèle base (ce qui masque le problème), laissez le modèle base transmettre tous les enregistrements, ajoutez un test qui détecte les doublons et déduplication dans la couche intermédiaire.
Le modèle base conserve tous les enregistrements :
-- base__crm__contacts.sqlSELECT id AS contact__id, email AS contact__email, updated_at AS contact__updated_at, _loaded_atFROM {{ source('crm', 'contacts') }}Un test détecte les doublons dans le modèle base :
models: - name: base__crm__contacts columns: - name: contact__id data_tests: - not_null data_tests: - dbt_utils.recency: datepart: day field: _loaded_at interval: 2Le modèle intermédiaire déduplique :
-- int__crm__contacts_deduplicated.sqlSELECT *FROM {{ ref('base__crm__contacts') }}QUALIFY ROW_NUMBER() OVER ( PARTITION BY contact__id ORDER BY contact__updated_at DESC) = 1Le test sur le modèle base se déclenche lorsque des doublons apparaissent, vous donnant de la visibilité. Le modèle intermédiaire les gère gracieusement quoi qu’il arrive. Si la source corrige son problème de duplication, le modèle intermédiaire continue de fonctionner correctement (la clause QUALIFY est sans effet lorsqu’il n’y a pas de doublons).
Coalescence de nulls
Certaines colonnes arrivent nulles alors qu’elles ne devraient pas l’être. Plutôt que de filtrer ces enregistrements dans la couche base (perte de données) ou de faire échouer le pipeline (blocage des modèles en aval), appliquez une coalescence avec des valeurs par défaut sensées dans la couche intermédiaire ou mart.
-- int__orders__enriched.sqlSELECT order__id, COALESCE(order__currency, 'USD') AS order__currency, COALESCE(order__status, 'unknown') AS order__status, order__total, order__created_atFROM {{ ref('base__payments__orders') }}Le choix de la valeur par défaut est important. 'unknown' pour un champ de statut est honnête : il indique aux consommateurs en aval « nous ne savons pas ». Fusionner vers une valeur valide spécifique comme 'pending' est dangereux car cela introduit des données qui semblent réelles mais ne le sont pas. La valeur par défaut doit être visiblement une valeur par défaut, et non quelque chose pouvant être confondu avec des données source réelles.
Ajoutez un test dans la couche base pour suivre la fréquence à laquelle la coalescence intervient :
models: - name: base__payments__orders columns: - name: order__currency data_tests: - not_null: config: severity: warnL’utilisation de severity: warn au lieu de severity: error permet au pipeline de continuer (le modèle en aval gère le null), mais vous conservez la visibilité via le routage d’alertes d’Elementary.
Filtrage des enregistrements invalides
Certains enregistrements sont véritablement invalides et doivent être exclus de l’analyse en aval. Enregistrements avec des dates impossibles, quantités négatives là où seules des valeurs positives ont du sens, enregistrements de test issus d’environnements QA. Filtrez-les dans la couche intermédiaire avec des conditions explicites et testables.
-- int__events__validated.sqlSELECT *FROM {{ ref('base__analytics__events') }}WHERE event__timestamp IS NOT NULL AND event__timestamp <= CURRENT_TIMESTAMP() AND event__timestamp >= '2020-01-01' AND user__id IS NOT NULLChaque condition WHERE est une décision de remédiation. Documentez-les, soit dans la description du modèle soit sous forme de commentaires en ligne, afin que la prochaine personne comprenne pourquoi les événements à date future sont exclus.
Pourquoi ce pattern est supérieur à la remédiation silencieuse
Le pattern observe-fix présente trois avantages par rapport aux approches qui corrigent les problèmes silencieusement.
Visibilité. Lorsque le test de la couche base émet un avertissement, vous savez que la source se comporte mal. Si vous corrigez silencieusement dans le modèle base, le problème pourrait persister pendant des mois avant que quelqu’un remarque la dégradation de la source. Le test vous donne un indicateur avancé.
Testabilité. Chaque technique de remédiation (déduplication, coalescence, filtrage) est un pattern SQL que vous pouvez tester unitairement de manière indépendante. Le framework de tests unitaires de dbt vous permet de vérifier que la logique de déduplication choisit le bon enregistrement, que la coalescence utilise la bonne valeur par défaut, que les conditions de filtrage correspondent à votre intention. La remédiation cachée dans une logique de retry ou dans des outils externes est beaucoup plus difficile à tester.
Auditabilité. Si un utilisateur demande « pourquoi ce client a-t-il la devise ‘USD’ alors qu’il est en Europe ? », vous pouvez retracer : le modèle base montre que la source a envoyé NULL pour la devise, le modèle intermédiaire l’a fusionné vers USD conformément à la valeur par défaut documentée. La lignée des données raconte l’histoire complète.
Les limites
Le pattern observe-fix gère les problèmes de qualité des données qui sont prévisibles et basés sur des règles. Les enregistrements dupliqués, les valeurs nulles, les dates hors plage. Vous savez à l’avance à quoi ressemble le problème et vous écrivez le SQL pour le gérer.
Il ne gère pas les modes de défaillance inédits : un changement de format de fichier, une nouvelle colonne qui casse une jointure, un problème d’encodage qui corrompt les données textuelles. Ceux-ci nécessitent une remédiation externe, soit via les niveaux supérieurs du spectre auto-correcteur soit via une investigation humaine.
Il ne remplace pas non plus une validation de la qualité des données appropriée. Le pattern observe-fix concerne la dégradation gracieuse : le pipeline continue de produire des résultats lorsque la source présente des problèmes connus. Ce n’est pas un substitut à des tests complets qui détectent les problèmes que vous n’avez pas anticipés.
Le pattern fonctionne mieux comme une couche dans une approche de défense en profondeur. Les retries gèrent les erreurs transitoires. L’adaptation aux dérives de schéma gère les changements structurels. Le pattern observe-fix gère les problèmes de contenu des données. La détection d’anomalies détecte ce que vous n’avez pas pensé à gérer explicitement. Chaque couche couvre différents modes de défaillance, et leur combinaison gère la majorité des incidents en production sans intervention d’IA ni intervention manuelle.