Est.

Documenting Metric Logic Inside dbt for Analytics Engineers

Document your metrics in dbt code, not scattered across dashboards and notebooks.

· 11 min read
Cover illustration for “Documenting Metric Logic Inside dbt for Analytics Engineers”
Semantic Layer Design · October 8, 2026 · 11 min read · 2,527 words

Day zero looks fine. A company launches its first dashboard, churn is defined one way, revenue is defined one way, and everyone nods along in the kickoff meeting. By day ninety, something has quietly rotted. A sales analyst pulled last quarter's SQL from an old dashboard, changed a filter to fit a new report, and shipped it without telling anyone what assumption got dropped in the process. Nobody reviewed it, because there was nothing set up to review. That's the actual mechanism behind metric sprawl: copy, tweak, ship, repeat, until ten dashboards each carry a slightly different version of "revenue."

The problem compounds because definitions never live in one place. One version sits in a dashboard filter. Another lives in a SQL script a data analyst keeps on their laptop. A third gets recalculated inside a notebook cell, and a fourth hides in a spreadsheet formula that someone built two years ago and nobody has touched since. Changing the business rule once means hunting down every one of those instances. Some get missed every time because there's no map telling them where all the copies are.

Marketing and Finance offer a clean illustration of how bad this gets before anyone notices. Marketing might define churn as a customer who hasn't purchased in a set number of days. Finance might define churn as a canceled subscription. Neither definition is written down anywhere both teams can see it, so the divergence stays invisible, right up until someone puts both numbers in the same slide deck and the room goes quiet.

A common response is "we already have a BI tool, and it works fine." That's true as far as it goes, but it misses the actual failure. A BI tool lets any analyst build any dashboard with any formula they want, and nothing in that tool forces two dashboards to agree with each other. Each one can fork its own version of a metric with no governance layer checking for convergence. The tool isn't the culprit. The absence of one authoritative, documented, reviewable definition is.

What dbt gives analytics engineers to document metric logic

dbt's response to this problem is to treat metric logic as code rather than as a dashboard configuration, and it does that through four distinct mechanisms, each one built to stop a specific kind of drift.

MetricFlow metric definitions handle the core of it: they encode not just what to compute, but how. That means specifying the entity, the measure, the filters, and the grain, all in one place. Because that definition is decoupled from any single BI tool, the same metric gets computed the same way whether the query comes from a dashboard, a Python notebook, or a spreadsheet pulling from the warehouse. One definition, consumed everywhere, instead of one dashboard's interpretation copied and mutated across the company.

Schema contracts define the interface at a model boundary. They state which columns will always exist, what type they'll be, and what constraints hold true. That gives anyone building downstream a guarantee about what they can safely rely on, rather than an assumption they have to double-check every time.

Column-level lineage answers a different question: where did this number come from? When an anomaly appears in a report, lineage lets someone trace it back through the chain of models that produced it, instead of guessing at which upstream change might have caused it.

The dbt Semantic Layer ties these pieces together. MetricFlow handles the SQL query construction and defines the specification for semantic models and metrics, so the heavy lifting, the joins, the aggregations, the filter logic, gets done once, upstream, instead of getting rebuilt slightly differently inside every dashboard that needs it.

Access modifiers and Groups add a design-time layer of control. Models can be organized into groups, each with a named owner, and marked private, protected, or public. That's a governance decision made at the project level, separate from whatever grants exist at the warehouse layer, and it controls which teams are allowed to reference a given model.

How YAML descriptions and MetricFlow definitions make metric logic reviewable

The real value of documenting metrics inside dbt comes from where that documentation lives: in version-controlled files, subject to the same review process as any other code change.

A MetricFlow definition written in YAML produces a diff. Someone proposes a change, a reviewer sees what shifted, and the change gets commented on, approved, or rejected before it ever reaches production. Compare that to a metric definition changed inside a dashboard. Nothing gets diffed. Nothing gets flagged for review. The change simply exists, silently, the next time someone opens that dashboard.

There's a useful test for whether a definition is actually doing its job: can a new team member, or an AI agent, read it and understand not just how to compute the metric, but why it's defined that way? If the answer is no, the definition isn't finished, no matter how syntactically correct it is.

A complete model description covers three things: the grain and purpose of the model, what the model is not (meaning the common misuses or misinterpretations that trip people up), and what changes downstream if the definition changes. That should run three to five sentences. Precise, not padded.

Take a revenue metric as an example. A good MetricFlow definition doesn't just sum a column. It follows an agreed formula, broken out by region, product, and channel, with refunds and currency conversion handled consistently inside the underlying model rather than left for each dashboard to interpret on its own. That consistency is the entire point: one formula, applied the same way everywhere, instead of five slightly different formulas scattered across five dashboards.

CI integration closes the loop on all of this. Tests run on every pull request, so a change to metric logic that would break a downstream relationship fails the build before it ever reaches a production dashboard. And because every change lives in git, there's a full audit trail: who changed a definition, when they changed it, and what the previous definition said. That audit trail is precisely what's missing in the day-zero-to-day-ninety drift story. Nobody could reconstruct what changed or when, because nothing was tracked.

Limits of YAML-First Metric Governance

None of this works automatically, and claiming otherwise would set teams up to be blindsided later. The YAML-first approach to metric governance has real limits, and understanding them ahead of time is what makes the approach durable.

The first limit is YAML sprawl itself. Crossing a few dozen metrics causes the YAML files to start multiplying the same way the SQL files once did. Standards drift. One analytics engineer names a dimension "customer_region," another names it "region_name," and suddenly the documentation layer that was supposed to solve inconsistency has its own inconsistency problem.

The second limit is what might be called the label problem. Renaming an existing SQL view as a "semantic layer" doesn't create governance on its own, since without enforcement behind the label, BI tools will keep forking local definitions exactly as before, just with a fancier name attached to the thing they're ignoring.

The third limit is a coverage ceiling. The Semantic Layer can only answer questions within the scope of what's actually been modeled. It guarantees correctness for the metrics it covers, but it has nothing to say about ad hoc questions that fall outside that scope. A well-governed semantic layer covering twenty metrics still leaves the twenty-first question unanswered, by design.

The deepest limit is organizational. If nothing stops a BI tool from forking a local definition anyway, the semantic layer is aspirational rather than operative. Teams can write the most precise YAML in the world, and if there's no enforcement mechanism, no CI check, no team norm, stopping a dashboard builder from reinventing a metric from scratch, the documentation becomes a reference nobody is required to consult. Governance that isn't enforced is theater.

Documenting everything at once isn't actually a fix. A partially modeled warehouse, where some metrics are rigorously governed and others aren't, can be worse than a warehouse with no governance. At least an ungoverned warehouse doesn't pretend to be trustworthy. A half-governed one creates false confidence, because people assume the semantic layer covers more than it does. That's the argument for sequencing the rollout deliberately, rather than trying to model the entire warehouse in one pass.

A practical rollout sequence: starting with the metrics that matter most

Given those limits, sequencing matters as much as the YAML itself. The right move is to ship one trusted metric before attempting to model the whole warehouse.

Start with the five to ten core metrics that generate the most disagreement, or draw the most stakeholder questions. Those are the metrics where documentation pays for itself fastest, because they're the ones already causing friction in meetings.

File structure is a real decision, not a formality. One option is a single YAML file per mart, co-locating documentation, data tests, unit tests, semantic models, and metrics in one place that corresponds to a dbt-modeled mart. That trades a larger file for less jumping between files, and it tends to suit smaller projects well. The other option is a dedicated semantic_models subfolder with a file structure that mirrors the rest of the project. That approach produces more targeted, smaller files, and it tends to suit larger projects migrating a lot of existing models. Neither is the universally right answer. The right one depends on how big the project already is and how much existing structure needs to be folded in.

For each metric in that first pass, document four things: the grain, the business rule behind it, what the metric is not (the common misinterpretations that cause arguments), and what changes downstream if the definition changes. Before merging any of it, apply the same test from the previous section: can a new team member, or an AI agent, read the definition and understand not just how to compute it, but why it's defined this way? If the answer is no, send it back before it ships.

Documenting the metric in MetricFlow only solves half the problem. The other half is enforcement: establish a team norm, and ideally a CI check, that flags new BI tool dashboards duplicating a definition that's already governed. If that step is skipped, the semantic layer and the dashboards will quietly diverge again, just on a longer timeline.

Migrating metrics and updating CI pipelines takes actual time, and nobody should pretend otherwise. Teams that have gone through it report the investment pays back in reduced incident response time, because fewer hours get spent reconciling conflicting numbers after the fact.

How documentation quality in dbt determines AI agent reliability

Something has shifted in what metric documentation is actually for. It used to be a courtesy to the next analyst. Now it's infrastructure that AI agents depend on directly, and the analytics engineer who writes it is effectively the person structuring the context those agents reason with.

AI agents have no contextual judgment to fall back on when a metric definition is ambiguous. A human analyst who hits a confusing metric can ask a colleague, apply institutional memory, or make an informed guess based on context. An agent encounters the same ambiguity and propagates it straight into an answer. A sparse or missing YAML description doesn't slow an agent down the way it might slow a person down. It just means the agent guesses, confidently, and moves on.

When an agent queries the dbt Semantic Layer, it queries against definitions that already encode the business rules, the joins, the filters, the grain. That deterministic query generation means an agent can't produce a subtly wrong aggregation for a metric that's already covered by the semantic layer, because the logic was fixed before the agent ever touched it.

The contrast appears clearly in practice. An agent querying raw tables with no semantic definitions behind them is making reasonable-sounding guesses, and those guesses are frequently wrong. An agent querying a governed Semantic Layer returns answers that are consistent and defensible, because the ambiguity was resolved upstream, in the documentation, long before the query ran.

That raises a genuinely new skill for analytics engineers: semantic precision. Writing a metric definition precise enough to be unambiguous to a human reader and to a model reasoning over it is not the same skill as writing clean SQL. Analytics engineers who build that skill are the ones whose work holds up inside an AI-native data stack.

The coverage trade-off from earlier carries forward here directly. The Semantic Layer guarantees correctness only for what's been modeled. Questions that fall outside that scope fall back to text-to-SQL, with all the accuracy risk that implies. The boundary between a governed query and an ungoverned one needs to be explicit, both to the people building the system and to the agents operating inside it, so nobody mistakes a guess for a guarantee.

Session materials previewing Okta's experience at dbt Summit 2026 pointed at exactly this lesson: getting production AI to work reliably depended less on swapping in a better model and more on having a governed, discoverable semantic layer that any agent could reason over from day one, built on dbt as the source of truth. The model wasn't the bottleneck. The documentation was.

What metric documentation governance means for data consumers

None of this exists to satisfy an audit. The entire point of documenting metric logic inside dbt is to give non-technical teams accurate answers they can get to themselves, without pulling an engineer into every question.

When metric logic is documented and governed upstream, the BI or analytics layer sitting on top of it can be used without anyone worrying whether the formula is right. That work gets done once, at the source, instead of getting redone, slightly differently, inside every dashboard built on top of it.

Without that governance, product, sales, and marketing teams run into conflicting numbers, stop trusting the data, and drift back to personal spreadsheets where at least the formula is one they understand, even if it's wrong in its own way. Engineering gets pulled back in to explain discrepancies or rebuild logic that should have been settled months earlier.

dbt deliberately doesn't try to be the layer business users touch directly. It isn't built for self-serve exploration, for computing metrics on the fly across arbitrary slices, or for embedding dashboards in front of stakeholders. Those jobs belong to the layer above dbt. What matters is that the layer above connects to the same governed definitions.

That points to a concrete choice when picking a BI layer: favor one that keeps metric definitions close to the models they depend on, rather than maintaining the same logic twice in two separate places. Two separate definition layers drifting apart from each other is just the original sprawl problem, recreated one level up the stack.

A well-documented semantic layer turns into a kind of force multiplier for the whole organization. An analyst goes on vacation, a team reorganizes, a new stakeholder shows up with a question nobody's asked before, and none of it requires rebuilding context from scratch. The documentation already holds the answer.

Sources

  1. Best practices
  2. Semantic models
  3. Build your metrics
  4. About MetricFlow
  5. Creating metrics
  6. dbt Semantic Layer
  7. Designing Reliable AI Agents for dbt Data Reviews

More in Semantic Layer Design