ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Anti-patterns des packages dbt

Erreurs courantes dans les packages dbt — schémas codés en dur, dispatch manquant, contraintes de versions trop strictes, noms de modèles génériques, matérialisations en table par défaut et version bounds absentes.

Planté
dbtdata engineeringdata modeling

Erreurs courantes dans les packages dbt qui brisent les principes de conception fondamentaux de la conception de packages — configurabilité, namespacing et adapter-awareness.

Références de schémas codées en dur

-- Mauvais : fonctionne dans votre projet, casse chez tout le monde
FROM my_database.raw_stripe.payments

C’est l’anti-pattern le plus courant. FROM my_database.raw_stripe.payments fonctionne dans votre projet parce que vous contrôlez le warehouse. Chaque autre utilisateur a un nom de base de données, un nom de schéma ou un nom de table différent.

Correction : Toujours utiliser source() avec var() pour la configuration du schéma et de la base de données. Voir les patterns de modèles packageables pour l’implémentation complète.

-- Bon : se résout selon la configuration de l'utilisateur
FROM {{ source('my_package', 'payments') }}
sources:
- name: my_package
schema: "{{ var('my_package_schema', 'raw_stripe') }}"
database: "{{ var('my_package_database', target.database) }}"

Le pattern source() + var() ajoute quelques lignes de YAML mais fait la différence entre un package qui s’installe partout et un qui ne fonctionne que sur votre machine.

Implémentations dispatch manquantes

Si votre package utilise du SQL qui varie selon le warehouse et que vous n’écrivez qu’une implémentation default__, les utilisateurs sur d’autres adapters obtiennent un comportement inattendu ou des erreurs.

-- Ne fonctionne que sur Snowflake/Postgres
{% macro default__my_date_trunc(datepart, date_expression) %}
DATE_TRUNC('{{ datepart }}', {{ date_expression }})
{% endmacro %}

Le DATE_TRUNC de BigQuery prend les arguments dans un ordre différent : DATE_TRUNC(date_expression, datepart). Un utilisateur BigQuery installant ce package obtient un bug de résultat incorrect silencieux ou une erreur de syntaxe selon l’expression.

Correction : Ajouter des implémentations dispatch pour chaque adapter que vous déclarez supporter. Si votre README dit « fonctionne sur Snowflake, BigQuery et Redshift », testez sur les trois.

{% macro bigquery__my_date_trunc(datepart, date_expression) %}
DATE_TRUNC({{ date_expression }}, {{ datepart }})
{% endmacro %}

Avant d’écrire des macros dispatch personnalisées, vérifiez si dbt Core fournit déjà un équivalent intégré. Depuis dbt-utils v1.0, les macros cross-database comme datediff, dateadd, safe_cast et hash se trouvent dans le namespace dbt. {{ dbt.datediff(...) }} gère les différences d’adapters pour vous.

Contraintes de versions trop strictes

# Force chaque utilisateur à utiliser exactement cette version
packages:
- package: dbt-labs/dbt_utils
version: "0.20.1"

L’épinglage sur une version spécifique oblige chaque utilisateur à utiliser exactement cette version et crée des conflits de dépendances avec d’autres packages. Si un autre package requiert dbt-utils >=1.0.0 et que le vôtre s’épingle à 0.20.1, dbt deps échoue avec un conflit de version que l’utilisateur doit déboguer.

Correction : Utiliser les plages de versions les plus larges qui fonctionnent réellement.

packages:
- package: dbt-labs/dbt_utils
version: [">=0.20.0", "<1.0.0"]

Testez votre package par rapport au minimum et au maximum de la plage dans votre matrice CI. S’il passe aux deux extrémités, la plage est sûre.

Noms de modèles génériques

Un modèle appelé customers entrera en collision avec le propre modèle customers de l’utilisateur. Un modèle appelé daily_summary entrera en collision avec tout autre package qui a jugé que daily_summary était un bon nom générique.

-- Collision en attente
-- models/customers.sql
SELECT * FROM {{ source('my_package', 'customers') }}

Correction : Préfixer tout avec le nom de votre package.

-- models/my_package__customers.sql
SELECT * FROM {{ source('my_package', 'customers') }}

Cela s’applique à tous les types de modèles : base, intermédiaire et mart. Voir les patterns de namespacing pour la convention complète. La verbosité est une fonctionnalité — revenue_tools__monthly_mrr est sans ambiguïté d’une façon que monthly_mrr ne sera jamais.

Matérialisation en table par défaut

dbt_project.yml
models:
my_package:
+materialized: table

Quand quelqu’un exécute dbt deps && dbt run, votre package ne devrait pas créer 30 tables physiques dans leur warehouse. Les tables consomment du stockage, prennent plus de temps à construire et créent des objets warehouse que l’utilisateur n’a pas demandés.

Correction : Par défaut, utiliser view dans le dbt_project.yml du package. Laisser les utilisateurs surcharger pour les performances.

models:
my_package:
+materialized: view

Les utilisateurs souhaitant la matérialisation en table peuvent surcharger dans leur propre projet :

models:
my_package:
+materialized: table

C’est l’inverse des recommandations normales pour les projets ordinaires, où table est la valeur par défaut recommandée. La différence est la propriété : dans votre propre projet, vous voulez de la visibilité pour le débogage et les performances de requête. Dans le projet de quelqu’un d’autre, vous voulez une empreinte légère que l’utilisateur peut personnaliser.

Pas de require-dbt-version

dbt_project.yml
name: 'my_package'
version: '0.1.0'
# require-dbt-version est absent

Sans require-dbt-version, les utilisateurs sur des versions dbt incompatibles obtiennent des erreurs de compilation obscures plutôt qu’un message clair. Un utilisateur sur dbt 1.1 essayant d’utiliser une fonctionnalité de dbt 1.6 verra un échec de compilation Jinja qui ressemble à un bug dans votre package plutôt qu’à une incompatibilité de version.

Correction : Toujours définir require-dbt-version avec une plage couvrant les versions que vous avez réellement testées.

require-dbt-version: [">=1.3.0", "<3.0.0"]

Avec le moteur Fusion (dbt 2.0) désormais disponible, [">=1.3.0", "<3.0.0"] est une valeur par défaut raisonnable couvrant à la fois dbt Core 1.x et dbt 2.x. Testez aux deux extrémités dans le CI pour vous en assurer.

Bonus : ne pas documenter les noms de variables

Ce n’est pas un anti-pattern de code, mais il est tout aussi courant. Si votre package a 15 appels var() et que le README ne les liste pas, les utilisateurs doivent lire votre code source pour savoir comment configurer le package.

Correction : Documenter chaque variable dans votre README avec son nom, son objectif et sa valeur par défaut. Un tableau fonctionne bien :

VariableDescriptionValeur par défaut
my_package_schemaSchéma où se trouvent les données source'my_data'
my_package_databaseBase de données pour les données sourcetarget.database
my_package__daily_summary_enabledActive le modèle de résumé quotidientrue

Les utilisateurs devraient pouvoir configurer votre package à partir du README sans lire un seul fichier SQL.