Author Image

CEO & Co-founder of Visivo

Killing Metric Drift: How to Build a Single Source of Truth for Metrics

When every team defines revenue differently, trust erodes. Here is how a code-defined metrics layer creates one definition that every dashboard and tool inherits.

Killing metric drift: building a single source of truth for metrics

A single source of truth for metrics means each KPI has exactly one definition, stored once and inherited everywhere it is used, so no two dashboards can quietly disagree about what "revenue" means. Metric drift, the slow divergence of the same KPI across teams and tools, is one of the top trust problems in analytics heading into 2026, and the fix is not better documentation. It is an enforced definition: one place every dashboard and tool has to read from, so consistency is a property of the system rather than a thing people remember to do.

This is an organizational problem first and a technical one second. A previous post argued why metrics belong in a semantic layer in principle. This one is about the failure mode that makes the principle urgent, metric drift, and how a single source of truth actually kills it.

The meeting where two dashboards disagree

You know the meeting. Someone pulls up the revenue dashboard. Someone else pulls up theirs. The numbers do not match. The room spends twenty minutes not on the decision they came to make, but on reconciling two charts that were both, technically, correct.

The finance dashboard netted out refunds. The sales dashboard counted gross bookings. The growth team's version excluded a region that had not been onboarded to billing yet. Every one of those choices was defensible when it was made. Nobody was wrong. And yet the organization just learned that it cannot agree on its single most important number in front of the people who most need to trust it.

That is the cost of metric drift, and it is rarely a one-time event. The first time leadership notices the numbers disagree, the damage is not the twenty minutes. It is that they start quietly discounting every number they see afterward, including the right ones. Inconsistent analyses do not just waste time. They erode the trust that makes analytics worth funding at all.

Why metric drift happens

Metric drift is not caused by careless people. It is the natural, predictable result of where metric definitions usually live.

In most setups, the meaning of a metric is embedded inside each report. The revenue calculation is a formula in a chart, a measure in a BI tool's metadata, a SUM() buried in a query somewhere. When the next person needs "revenue," they do not inherit the existing definition. They rebuild it, from memory or from a quick look at someone else's chart, and they make a slightly different choice. Refunds in or out. This region or that one. Trailing thirty days or calendar month.

Now you have two definitions of revenue. Neither author knows the other exists. Multiply that across margin, active users, churn, conversion, and every other metric that matters, across every department that tracks them, and drift is not a risk. It is the default state of any organization that has not deliberately engineered against it. The more successful and bigger you get, the more teams, the more reports, and the more drift, unless something structural holds the line.

Documentation is not enforcement

The common first response is to write it down. A metrics dictionary. A wiki page titled "Official Metric Definitions." A spreadsheet finance maintains. These are well-intentioned and they do help a little, but they share one fatal flaw: documentation describes, it does not enforce.

A wiki page that says "revenue excludes refunds" has zero power to stop someone from building a chart that includes them. The page and the chart are completely disconnected. Nobody is forced to read the doc before they build, the doc goes stale the moment a definition changes and someone forgets to update it, and even a perfectly maintained doc cannot prevent a single divergent query from shipping. You are relying on every analyst, forever, to look up the right definition and apply it correctly by hand. That is not a system. That is hope.

The best semantic layers understand this distinction. They do not just describe the right definition. They are the definition. A chart cannot use the wrong revenue number because the only revenue number available is the one in the layer. Enforcement comes from the architecture, not from discipline. That is the move that actually kills drift, and it is the through-line of how we think about reliable BI insights for stakeholders: consistency has to be structural, not aspirational.

Defining a metric once, in code

So what does an enforced definition look like? You define the metric one time, in code, in your repository, and everything downstream references it by name instead of re-deriving it.

models:
  - name: orders
    sql: "SELECT * FROM orders"
    metrics:
      - name: net_revenue
        expression: "SUM(amount - refunds)"
    dimensions:
      - name: order_month
        expression: "DATE_TRUNC('month', created_at)"

net_revenue is now a named thing with one definition. A dashboard does not recompute it. It asks for it. There is exactly one place the formula lives, exactly one place to change it, and therefore exactly one answer to "what is revenue," because there is exactly one net_revenue and no way to invent a second by accident.

Putting the definition in code, in Git, is what turns it from a description into an enforced contract. The definition is the same object the charts consume, so they cannot diverge from it. And because it is code, a change to it shows up as a diff in a pull request: a human reviews exactly what changed about revenue, approves it, and the history records who changed it and when. Six months later, when someone asks why a number restated, the answer is in the commit log. This is the same discipline we apply to tracking dashboard changes through pull requests, pushed all the way down to the metric.

Reuse across dashboards and tools

The single source of truth pays off twice. First in consistency, then in maintenance.

Once net_revenue is defined once, every dashboard, every Insight, and every teammate uses the same one. A new chart inherits the correct definition for free, because the correct definition is the only one on offer. There is no opportunity to drift, because there is no second definition to drift toward. Define once, reuse everywhere.

Maintenance collapses too. When finance decides that discounts should also be netted out of revenue, you do not go on a scavenger hunt across dozens of reports hoping you found them all. You edit net_revenue in one place, and every chart that references it is correct on the next run. Definitions also compose across models through relations, so a metric like revenue-per-customer is built from existing definitions rather than re-deriving the joins by hand:

relations:
  - name: orders_to_customers
    condition: ${ref(orders).customer_id} = ${ref(customers).id}

metrics:
  - name: revenue_per_customer
    expression: "${ref(orders).net_revenue} / ${ref(customers).active_count}"

revenue_per_customer builds on net_revenue, so it can never disagree with the headline revenue figure. The composition inherits the single source of truth automatically. This is the same lesson that made dbt™ the backbone of the modern warehouse, applied to the metrics layer that sits above it: define transformations once, reference them everywhere, and let the dependency graph keep everything consistent.

How Visivo keeps definitions in one place

Visivo's semantic layer is built to be that single source of truth. Metrics, dimensions, and relations are defined in YAML, stored in your Git repository, and consumed by every Insight and dashboard in the project. A metric is defined once on a model. Every chart that needs it references it by name. There is no separate, divergent copy living inside a chart, because in Visivo the chart does not hold a definition at all. It points at the one in the layer.

That makes the definition enforced rather than merely documented. The viewer renders what the semantic layer says, so a stakeholder slicing the data with filters and splits is always working from the canonical metric. Change the definition through a pull request, and the change propagates everywhere on the next run, with the full review and history that comes from it being code. You run it locally with visivo serve, review changes in PRs, and deploy with visivo deploy -s [stage], all on top of one set of definitions.

If you want to see it, the examples gallery is built entirely on this model, /get-started takes you from install to your first shared metric in a few minutes, and the semantic layer is documented in full at docs.visivo.io.

The next time two dashboards disagree about revenue, do not open a doc and do not call a meeting. Move the definition to a place they all have to read from, and make drift impossible by construction.

Previously in Visivo

Last week's perspective, the rise of in-process analytics, looked at the engine layer powering fast local dashboards. This week moves up a level to the definition layer that keeps those dashboards honest. For the principle behind it, read why your metrics belong in a semantic layer.

Install command copied