La règle des trois est une heuristique pour décider quand extraire une macro dbt : attendre la troisième occurrence d’un pattern avant de l’abstraire. L’abstraction précoce tend à créer des macros qui obscurcissent le SQL sans apporter de bénéfice réel de réutilisation.
La règle
Première occurrence : écrivez-la inline. Pas de macro. Juste du SQL.
Deuxième occurrence : notez le pattern. Ajoutez peut-être un commentaire : « Logique similaire dans base__shopify__orders. » Toujours pas de macro.
Troisième occurrence : vous avez maintenant suffisamment d’informations pour extraire quelque chose d’utile. Vous avez vu comment le pattern est réellement utilisé dans de vrais modèles. Vous savez quelles parties varient réellement et lesquelles restent constantes. Vous pouvez maintenant construire une abstraction qui correspond à la réalité plutôt que de deviner ce dont vous pourriez avoir besoin.
Cette règle vient de la pratique du génie logiciel, et la documentation propre à dbt valide le principe sous-jacent : « Favorisez la lisibilité lors du mélange de Jinja avec SQL, même si cela implique de répéter certaines lignes. »
À quoi ressemble la progression
-- Première fois : inlineSELECT order_id, ROUND(amount_cents / 100.0, 2) AS order__amount_dollarsFROM {{ ref('base__shopify__orders') }}-- Deuxième fois : toujours inline, mais on remarque le patternSELECT payment_id, ROUND(amount_cents / 100.0, 2) AS payment__amount_dollarsFROM {{ ref('base__stripe__payments') }}-- Troisième fois : on extrait maintenant la macro{% macro cents_to_dollars(column_name, scale=2) %} ROUND({{ column_name }} / 100.0, {{ scale }}){% endmacro %}Notez qu’attendre jusqu’à la troisième utilisation a révélé quelque chose d’important : le paramètre scale. La première fois qu’on voit ce pattern, on pourrait coder en dur 2. C’est seulement après l’avoir vu utilisé dans différents contextes qu’on réalise que certains appelants pourraient vouloir 4 décimales pour des calculs sensibles à la précision. La règle des trois fait émerger les paramètres dont on a réellement besoin par rapport à ceux qu’on imagine pouvoir avoir besoin.
La tentation de sauter les étapes
La tentation est forte. On voit le pattern le premier jour et on pense : « Je vais sûrement utiliser ça encore. » Parfois c’est vrai. Plus souvent, les deuxième et troisième utilisations ont des différences subtiles qui cassent l’abstraction initiale.
L’échec classique : on extrait une macro après deux utilisations, on la conçoit autour de ces deux cas, puis la troisième utilisation nécessite une variante légèrement différente. On ajoute maintenant un paramètre optionnel pour gérer l’exception. La quatrième utilisation en nécessite un autre. À la sixième utilisation, on a une macro avec cinq paramètres et des branches de logique conditionnelle, et c’est plus difficile à comprendre que du SQL inline.
Attendre permet de construire la bonne abstraction plutôt que celle qu’on imaginait.
Le coût des macros prématurées
Les macros prématurées créent trois problèmes qui se cumulent.
Lisibilité réduite. Chaque référence de macro est une rupture de contexte mentale. Le lecteur doit ouvrir un autre fichier, comprendre la logique de la macro, puis revenir au modèle. Avec du code inline, tout est visible en un seul endroit. Un modèle qui nécessite la lecture de quatre fichiers de macros juste pour comprendre une seule requête est illisible.
Complexité accrue. Pour gérer tous les cas qu’on imaginait, on ajoute des paramètres. Chaque paramètre est un point de décision. Une macro avec sept paramètres est plus difficile à utiliser correctement que du code inline — et plus difficile à tester, et plus difficile à documenter.
Code fragile. Quand une macro sert cinq modèles, les changements font peur. On veut corriger le comportement pour un modèle mais ne peut pas risquer de casser les autres. Alors on ajoute un autre paramètre, aggravant le problème de complexité. La macro devient une infrastructure portante que personne ne veut toucher.
Ces trois problèmes ne sont pas indépendants. La complexité aggrave la fragilité ; la fragilité aggrave la lisibilité ; la mauvaise lisibilité cache la complexité qui rend les choses fragiles. L’abstraction prématurée est un piège qui se referme sur lui-même.
Quand briser la règle
Trois occurrences est une heuristique, pas une loi. Il y a des raisons légitimes d’extraire une macro plus tôt :
Patterns sensibles à la sécurité. Si le pattern contient quelque chose qui ne doit jamais varier — un nom de schéma codé en dur, une approche de chiffrement — extrayez-le immédiatement pour qu’il y ait une version faisant autorité.
Boilerplate sans variation. Certains patterns sont véritablement invariants. add_audit_columns ajoute les mêmes champs de métadonnées à chaque table. Il n’y a pas de « deuxième utilisation avec des différences subtiles » à attendre.
Conventions d’équipe. Lors de l’intégration d’une équipe à un nouveau pattern de projet — disons une approche cohérente de gestion des enregistrements supprimés d’une source SaaS — on peut définir la macro dès le départ comme signal d’intention : « c’est comme ça qu’on fait ici. »
Même dans ces cas, gardez la macro simple. La justification de l’extraction précoce est que le pattern est bien compris et invariant — pas qu’on est confiant sur tous les cas qu’il pourrait avoir besoin de gérer.
Le principe sous-jacent
La règle des trois concerne la collecte d’informations avant l’abstraction. À la troisième utilisation, la variation réelle du pattern est visible : ce qui change entre les points d’appel, ce qui reste constant, et quels cas limites produisent les vrais modèles. Ces informations produisent une abstraction ancrée dans l’utilisation réelle plutôt que dans le besoin anticipé.
Voir Macros dbt à responsabilité unique pour ce qu’il faut faire une fois l’extraction justifiée, et Conventions de nommage des macros dbt pour les patterns de nommage qui rendent les macros découvrables.