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.sqlSELECT DATE_TRUNC('month', ordered_at) AS month, SUM(amount) AS monthly_revenueFROM {{ ref('mrt__finance__orders') }}GROUP BY 1Ce 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_amountMaintenez 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 ligneSi 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: sessionsLa 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 :
# Inflexiblemeasures: - name: enterprise_revenue expr: CASE WHEN segment = 'enterprise' THEN amount END agg: sumCette 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_valueQue 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_valueLes bonnes descriptions répondent à trois questions :
- Que mesure-t-elle ? « Somme des valeurs contractuelles annualisées » — pas juste « ARR ».
- Qu’exclut-elle ? « À l’exclusion des frais uniques et des charges à l’usage ».
- À 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: sessionsCela 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.