Data Engineering  /  dbt

๐Ÿ”„ dbt โ€” Data Build Tool 60 guides ยท updated 2026

Analytics engineering with SQL โ€” models, tests, sources, and Jinja macros that turn raw warehouse tables into trustworthy, documented data products.

dbt Metrics Layer: Defining Business Metrics Once, Reusably

Ask five analysts to write a query for โ€œmonthly active usersโ€ and youโ€™ll likely get five subtly different definitions โ€” different date boundaries, different inclusion criteria for what counts as โ€œactive,โ€ different handling of edge cases. Every one of them might be defensible individually, and the resulting inconsistency between dashboards is one of the most common sources of โ€œwhy do these two reports show different numbers for the same metricโ€ disputes. dbtโ€™s metrics layer exists to define a metricโ€™s logic exactly once, as code, so every tool querying it gets the same answer.


The Problem: Metric Logic Scattered Everywhere

Without a metrics layer, โ€œmonthly revenueโ€ logic typically lives in multiple places simultaneously: hardcoded in a Looker LookML file, redefined slightly differently in a Tableau calculated field, and written a third way directly in an analystโ€™s ad hoc SQL query. Each definition can drift independently, and thereโ€™s no single source of truth to reconcile them against when numbers disagree.


Defining a Metric

models/marts/_metrics.yml
metrics:
- name: monthly_revenue
label: "Monthly Revenue"
model: ref('sales_summary')
description: "Total completed order revenue, recognized monthly."
calculation_method: sum
expression: revenue
timestamp: month
time_grains: [month, quarter, year]
filters:
- field: order_status
operator: '='
value: "'completed'"

This single YAML declaration says, precisely: revenue means the sum of the revenue column from sales_summary, only counting completed orders, aggregatable by month, quarter, or year. Any tool querying this metric definition gets identical logic โ€” no more reconciling five slightly different SQL queries that all claim to compute โ€œrevenue.โ€


Querying a Defined Metric

Rather than writing raw SQL against the underlying table, metrics are queried through dbtโ€™s metric interface, which compiles the correct SQL from the declaration automatically.

Terminal window
dbt run-operation calculate_metric --args '{"metric": "monthly_revenue", "grain": "month"}'

The exact invocation pattern depends on your dbt version and whether youโ€™re using the newer Semantic Layer (covered in Semantic Layer), but the core idea holds across implementations: the metricโ€™s calculation logic lives in one YAML definition, queried consistently rather than re-implemented per consumer.


Metrics Built From Other Metrics

A more advanced pattern lets you derive new metrics from existing ones, rather than redefining the underlying logic each time.

metrics:
- name: revenue_per_customer
label: "Revenue Per Customer"
calculation_method: derived
expression: "{{ metric('monthly_revenue') }} / {{ metric('active_customer_count') }}"

This guarantees revenue_per_customer always uses the exact same monthly_revenue and active_customer_count definitions declared elsewhere โ€” if either underlying metricโ€™s logic changes, this derived metric automatically reflects the update, rather than needing its own separate maintenance.


Why This Belongs in dbt, Not in the BI Tool

The instinctive place to define a metric is inside the BI tool itself โ€” a Looker measure, a Tableau calculated field. The problem is that metric logic then only exists for users of that specific tool. A data scientist querying the warehouse directly from a notebook, or a second BI tool the company adopts later, has no access to that definition and has to reimplement it independently โ€” exactly the drift problem this feature exists to prevent. Defining metrics in dbt, adjacent to the models and tests that already define what the data means, keeps the definition tool-agnostic.


Metrics and Testing

Because a metric definition is just YAML referencing an underlying model and column, it inherits the reliability of whatever testing exists on that model โ€” a not_null and range test on the revenue column, covered in Generic Tests and Custom Tests, directly protects the accuracy of any metric built on top of it.


Metrics Layer vs. Semantic Layer: A Quick Distinction

The Metrics Layer, covered here, is specifically about defining individual metric calculations. dbtโ€™s broader Semantic Layer, covered in Semantic Layer, builds on top of this with a more complete semantic model โ€” dimensions, entities, and multi-tool query serving via MetricFlow โ€” solving the same underlying consistency problem at a larger architectural scale.

Metrics LayerSemantic Layer
ScopeIndividual metric definitionsFull semantic model โ€” metrics, dimensions, entities
Solvesโ€Define this calculation once""Serve consistent metrics to any tool that asksโ€

Documenting Metrics Alongside Models

Metrics benefit from the same descriptive documentation practice as models and columns โ€” a description field on each metric definition, surfaced in the documentation site covered in dbt Docs Generate, gives anyone querying the metric a clear, authoritative explanation of exactly what it measures.

metrics:
- name: monthly_revenue
description: >
Total completed order revenue, recognized in the month the order
was marked complete (not the month it was placed, if different).
Excludes cancelled and refunded orders entirely.

This kind of precise description โ€” calling out the specific recognition timing and exclusions โ€” is exactly the detail that prevents two different analysts from assuming slightly different things about what โ€œrevenueโ€ includes, even once the calculation itself is centrally defined and consistent.

Common Mistakes

Defining a metricโ€™s logic in the BI tool anyway, โ€œjust for now.โ€ This immediately reintroduces the exact drift problem the metrics layer exists to prevent โ€” the discipline only pays off if metric logic actually lives in one declared place.

Not testing the underlying columns a metric depends on. A metric is only as reliable as the data feeding it โ€” an untested revenue column with silent data quality issues produces a confidently wrong metric, not a visibly broken one.

Redefining a derived metricโ€™s components manually instead of referencing existing metrics. This defeats the reuse benefit โ€” always build derived metrics by referencing other declared metrics, not by re-expressing their underlying logic independently.

Summary

ConceptPurpose
Metric definition (YAML)Declares a business calculation exactly once
Derived metricsBuild new metrics from existing ones without re-deriving logic
Tool-agnostic queryingAny consumer gets the same definition, not a per-tool reimplementation

A defined metrics layer turns โ€œwhy do these two dashboards disagreeโ€ from a recurring investigation into a question with a structural answer: they canโ€™t disagree, because theyโ€™re both querying the same declared definition.