Skip to main content
Back to Blog

Using dbt Metrics and the Semantic Layer with AI-Assisted Analytics

The metric definition problem in analytics is older than the tooling that tries to solve it. Two teams look at the same dashboard, see different numbers because they are using slightly different filters, and spend a meeting arguing about whose number is right. Everyone agrees this is bad. The solutions that existed before the dbt semantic layer either pushed metric logic into the BI tool (Looker LookML, Tableau calculated fields) or pushed it into the warehouse as materialized views that someone had to maintain.

dbt's semantic layer, built on top of the MetricFlow engine, takes a different approach. Metrics are defined in YAML in your dbt project, and they are queryable through a standard API that any downstream tool can use. The definitions compile to SQL at query time and execute in your warehouse. The warehouse is the execution engine. The semantic layer is the translation layer between business terminology and warehouse SQL.

How Metric Definitions Work in dbt MetricFlow

A metric definition in dbt's semantic layer has several required components. The measure specifies what to aggregate (a SUM, COUNT, COUNT_DISTINCT, or custom expression) against a source model. The grain specifies the primary time dimension for the metric. And the dimensions specify which attributes can be used to filter or group the metric.

A minimal conversion rate metric definition looks roughly like this:

metrics:
  - name: conversion_rate
    type: ratio
    type_params:
      numerator: converted_users
      denominator: total_users
    label: "Conversion Rate"
    description: "Fraction of users who completed onboarding within 14 days"

The converted_users and total_users here reference measures defined on the underlying semantic model, which is where the actual SQL expression lives. The metric definition composes those measures. When a downstream tool queries conversion_rate, MetricFlow generates the appropriate SQL based on the grain and dimensions requested, runs it against the warehouse, and returns the result.

The critical property of this setup is that the definition is the single source of truth. A BI tool querying the semantic layer gets the same conversion rate as an attribution tool querying the semantic layer. They are running the same SQL, generated from the same definition, against the same warehouse. The "whose number is right" problem is structurally eliminated.

Where Attribution and the Semantic Layer Connect

Metric attribution requires a consistent metric definition and a dimensional schema. Both of those exist in the dbt semantic layer. A metric attribution system that reads from the semantic layer instead of constructing its own queries gets both for free.

The dimensional schema in MetricFlow is defined through entities and dimensions on semantic models. An entity is a join key (a user ID, an order ID). A dimension is an attribute that can be used to slice the metric. When you define a dimension as type: categorical with categorical values like device_type or plan_tier, that dimension becomes available for attribution decomposition without any additional configuration.

The connection point between Golden Analytics and dbt is the semantic layer API. We read metric definitions to understand the canonical metric computation, and we read the dimensional schema to know which dimensions are valid attribution cuts for each metric. When attribution runs, it generates queries that MetricFlow would recognize as valid metric queries, ensuring that the attribution results match what the same metric would show in any other tool using the semantic layer.

Practical Setup Considerations

Getting attribution to work well on top of the dbt semantic layer requires attention to a few details that are easy to overlook when initially defining metrics.

Grain and attribution windows

Most metrics are defined at a daily grain. Attribution questions are frequently period-over-period: last week vs. the prior week, or this month vs. last month. The attribution engine needs to aggregate the daily grain metric up to the comparison period. This is straightforward for additive metrics like revenue. For ratio metrics like conversion rate, the aggregation needs to be done correctly: you sum numerator and denominator separately across the period and then divide, rather than averaging the daily rates. The semantic layer handles this if the metric is typed as ratio, but it is worth verifying that your metric type is correct before running attribution.

Dimension cardinality and attribution performance

Attribution runs a dimensional decomposition across all dimensions that are available for a metric. If you have defined 20 dimensions on a semantic model, attribution will attempt to compute mix and rate effects across all 20 simultaneously. For high-cardinality dimensions (city with 200 values, user cohort with 50 weekly buckets), the query fan-out can be significant.

The practical approach is to define attribution-relevant dimensions explicitly, either through a dimension tagging convention in your dbt project or through an attribution configuration that specifies which dimensions to include. Not every dimension that is valid for filtering is useful for attribution. Geographic granularity at the city level is rarely the right starting point for attribution; start at region or country and drill in after the primary driver is identified.

Exposure and data freshness

The semantic layer queries run at request time against whatever data is in your warehouse at that moment. If your dbt models refresh once daily, the semantic layer and attribution results will reflect yesterday's data until the next refresh completes. For attribution questions about last week's performance (the most common case), daily refresh is usually sufficient. For near-real-time monitoring use cases, you need either more frequent dbt model refreshes or a different data path that bypasses the semantic layer's dependency on transformed models.

The Governance Benefit: What Changes at the Organizational Level

The technical benefit of the dbt semantic layer and attribution integration is consistent metric computation. The organizational benefit is harder to quantify but arguably more important.

When metric definitions are in version-controlled YAML in a dbt project, changes to those definitions go through the same review process as any other code change. If someone wants to change the conversion rate definition to use a 7-day onboarding window instead of 14 days, that change shows up in a pull request, gets reviewed by the data team, and the change history is preserved in git. This is fundamentally different from a Looker LookML change or a Tableau calculated field edit, both of which typically have weaker change management and no automatic audit trail.

For attribution specifically, this matters because attribution results are often used in business reviews and reporting that has real consequences. If the conversion rate the VP of Product sees in their weekly attribution report changes because someone modified the metric definition without communication, that creates confusion and erodes trust in the numbers. Version-controlled metric definitions make that class of silent change visible and preventable.

What the Semantic Layer Does Not Solve

The semantic layer solves the "which SQL expression should I run" problem. It does not solve the "which metric should I care about" problem, or the "why does the business define this concept differently across teams" problem.

If two teams have genuinely different definitions of "active user" and both definitions are valid for their respective purposes, the semantic layer can encode both. But someone has to decide which one the attribution report uses, and that decision requires human judgment about which definition is more relevant for the question being asked. The semantic layer is neutral: it stores both definitions and queries them correctly. It does not adjudicate between them.

There is also an ongoing maintenance cost. Semantic model definitions need to stay current as upstream table schemas change. If a source model column is renamed or dropped, the semantic model that references it breaks, and every downstream query that uses that metric fails. The semantic layer adds a dependency layer that, like any dependency layer, requires maintenance when the thing it depends on changes. That maintenance responsibility should be owned explicitly by the data engineering team, not left as an implicit assumption.