ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Anti-patterns de métriques dans dbt

Erreurs courantes lors de la définition de métriques MetricFlow — modèles ad hoc pour les métriques, erreurs de somme de ratios, filtres codés en dur dans les mesures, et descriptions manquantes

Planté
dbtdata modelinganalytics

Ces anti-patterns MetricFlow apparaissent de façon récurrente dans les projets qui ont grandi sans stratégie claire pour les métriques. Chacun fonctionne techniquement mais crée des problèmes à l’échelle.

Modèles ad hoc pour les métriques

L’anti-pattern le plus courant : créer un nouveau modèle dbt uniquement pour définir une métrique.

-- Mauvais : models/metrics/monthly_revenue.sql
SELECT
DATE_TRUNC('month', ordered_at) AS month,
SUM(amount) AS monthly_revenue
FROM {{ ref('mrt__finance__orders') }}
GROUP BY 1

Ce modèle duplique une logique de transformation qui appartient à la couche sémantique. Le mart des commandes dispose déjà de la colonne amount et de l’horodatage ordered_at. Une métrique MetricFlow sur ce mart gère l’agrégation et la granularité temporelle nativement. Le modèle ad hoc ajoute une table à votre entrepôt, un nœud à votre DAG, et une charge de maintenance — tout cela pour quelque chose que MetricFlow calcule à la volée.

Le pattern est particulièrement insidieux parce qu’il fonctionne. La table existe. Les tableaux de bord peuvent la requêter. Mais vous avez maintenant deux endroits où le « revenu mensuel » est calculé : le modèle ad hoc et la définition de métrique qui sera éventuellement créée. Ils divergeront.

La correction est simple : définissez la métrique sur votre mart existant et laissez MetricFlow gérer l’agrégation.

metrics:
- name: monthly_revenue
label: "Monthly Revenue"
description: "Sum of order amounts, aggregated monthly"
type: simple
type_params:
measure: order_amount

Maintenez votre structure de projet propre en évitant les modèles ad hoc qui n’existent que pour le calcul de métriques. Si un calcul nécessite un modèle, celui-ci doit être un modèle intermédiaire ou mart réutilisable — pas une agrégation à usage unique qui duplique le travail de la couche sémantique.

Somme de ratios

Cet anti-pattern est mathématiquement dangereux car il produit des résultats silencieusement incorrects.

# Incorrect : donnera des résultats mathématiquement incorrects
- name: avg_conversion_rate
type: simple
type_params:
measure: conversion_rate # C'est déjà un pourcentage par ligne

Si la table sous-jacente a une colonne conversion_rate pré-calculée (conversions divisées par sessions par ligne), calculer la moyenne de ces pourcentages donne un résultat incorrect. Le magasin A avec 100 sessions et 50 % de conversion + le magasin B avec 10 000 sessions et 2 % de conversion ne font pas en moyenne 26 %. Le taux global correct est plus proche de 2,5 %, pondéré par le volume de sessions.

La correction est une métrique ratio avec des mesures de numérateur et dénominateur séparées :

measures:
- name: conversions
agg: sum
- name: sessions
agg: sum
metrics:
- name: conversion_rate
type: ratio
type_params:
numerator: conversions
denominator: sessions

La couche sémantique calcule le ratio après avoir agrégé les composants. C’est mathématiquement correct quel que soit le groupement des données par la requête.

La règle : n’agrégez jamais un ratio pré-calculé. Conservez toujours le numérateur et le dénominateur comme mesures séparées et laissez MetricFlow les diviser au moment de la requête.

Filtres codés en dur dans les mesures

Les mesures avec des filtres intégrés réduisent la flexibilité et créent des hypothèses cachées :

# Inflexible
measures:
- name: enterprise_revenue
expr: CASE WHEN segment = 'enterprise' THEN amount END
agg: sum

Cette mesure ne peut calculer que le revenu enterprise. Si le marketing a besoin du revenu PME, il faut une nouvelle mesure. Si l’équipe dirigeante veut le revenu startup, encore une autre mesure. Chacune duplique l’agrégation de amount avec un filtre différent.

Mieux vaut définir une mesure générale et appliquer les filtres au niveau de la métrique :

measures:
- name: revenue
expr: amount
agg: sum
metrics:
- name: enterprise_revenue
type: simple
type_params:
measure: revenue
filter:
- "{{ Dimension('customer__segment') }} = 'enterprise'"
- name: smb_revenue
type: simple
type_params:
measure: revenue
filter:
- "{{ Dimension('customer__segment') }} = 'smb'"

Vous pouvez désormais créer n’importe quelle métrique de revenu par segment sans définir de nouvelles mesures. La mesure de base reste propre et réutilisable. Le filtre réside où il doit être — au niveau de la métrique, où il est visible et documenté.

Cela rend également les calculs de ratios plus propres. Un taux de conversion filtré par segment utilise les mêmes mesures générales conversions et sessions avec des filtres par segment, plutôt que de nécessiter des mesures séparées enterprise_conversions et enterprise_sessions.

Descriptions manquantes

Les métriques sans descriptions deviennent des mystères en quelques semaines :

- name: arr
type: simple
type_params:
measure: arr_value

Que comprend arr ? Est-ce le revenu récurrent mensuel annualisé ? Cela ne compte-t-il que les abonnements actifs ? Les frais uniques sont-ils exclus ? Les charges à l’usage ? Les comptes en période d’essai ?

Sans description, quelqu’un répondra à ces questions en lisant le SQL du modèle sous-jacent, en traçant la définition de la mesure, et peut-être en vérifiant la logique du mart. C’est 15 minutes de travail de détective qu’un seul paragraphe suffirait à éliminer.

Incluez toujours description et label :

- name: arr
label: "Annual Recurring Revenue"
description: >
Sum of annualized contract values for active subscriptions,
excluding one-time fees and usage-based charges. Matches
the finance team's ARR definition as of Q1 2026.
type: simple
type_params:
measure: arr_value

Les bonnes descriptions répondent à trois questions :

  1. Que mesure-t-elle ? « Somme des valeurs contractuelles annualisées » — pas juste « ARR ».
  2. Qu’exclut-elle ? « À l’exclusion des frais uniques et des charges à l’usage ».
  3. À quelle définition correspond-elle ? « Correspond à la définition ARR de l’équipe finance » — c’est le contrat social qui prévient la dérive des métriques.

Les descriptions ne sont pas une charge documentaire. Elles sont le mécanisme par lequel le metrics-as-code génère la confiance. Une définition de métrique sans description est un calcul sans contexte — techniquement correct mais pratiquement inutile pour la gouvernance.

Suremploi des métriques dérivées pour les ratios

Une variante plus subtile du problème de somme de ratios. Les équipes utilisent parfois des métriques dérivées pour des calculs qui devraient être des métriques ratio :

# Dangereux : se comporte incorrectement quand groupé par dimensions
- name: conversion_rate
type: derived
type_params:
expr: conversions / sessions
metrics:
- name: conversions
- name: sessions

Cela paraît correct et produit le bon chiffre au niveau total. Mais quand on groupe par une dimension (région, type d’appareil), MetricFlow peut évaluer l’expression au niveau de la métrique individuelle plutôt qu’agréger d’abord le numérateur et le dénominateur. Le résultat dépend des détails d’implémentation et peut produire l’erreur de somme de ratios.

Utilisez type: ratio pour toute métrique « X divisé par Y ». Il existe spécifiquement pour gérer correctement l’ordre d’agrégation.

Chaque anti-pattern partage une cause racine : les métriques traitées comme des calculs ad hoc plutôt que comme des artefacts réutilisables. L’approche metrics-as-code considère les métriques comme consommées par plusieurs outils, groupées par des dimensions arbitraires, et maintenues par des personnes qui ne les ont pas écrites — un cadre qui prévient l’accumulation de modèles ad hoc, de ratios pré-calculés, de filtres codés en dur et de définitions non documentées.