
CTO & Co-founder of Visivo
Why Your Metrics Belong in a Semantic Layer, Not in Every Dashboard
Copy-pasting SUM(revenue) into every dashboard is how metrics drift. A semantic layer makes a metric a single, testable definition the whole org shares.

Here is a question that has quietly cost data teams millions of hours: what is revenue?
Ask three dashboards and you will get three answers. The finance dashboard nets out refunds. The sales dashboard counts gross bookings. The board deck uses whatever number someone pasted into a chart eight months ago and never revisited. Each one is defensible in isolation. Together, they are a slow-motion credibility crisis, because the first time an executive notices the numbers disagree, they stop trusting all of them.
The instinct is to call a meeting and "align on definitions." That is treating a symptom. The real problem is architectural: the definition of a metric lives inside each dashboard instead of in one place every dashboard reads from. A semantic layer fixes the architecture, not just this week's discrepancy.
What a semantic layer actually is
Strip away the jargon and a semantic layer is one idea: a metric is a named, versioned definition that lives in your codebase, and everything that needs that number references the name, never the underlying SQL.
models:
- name: orders
sql: "SELECT * FROM orders"
metrics:
- name: net_revenue
expression: "SUM(amount - refunds - discounts)"
dimensions:
- name: order_month
expression: "DATE_TRUNC('month', created_at)"
net_revenue is now a thing with a name. A chart does not re-derive it; it asks for it. There is exactly one place the formula lives, exactly one place to change it, and exactly one answer to "what is revenue," because there is exactly one net_revenue.
This is not a new idea. It is the same move software engineers made decades ago when they stopped copy-pasting logic and started extracting functions. We just took a long time to apply it to analytics. I have argued before that the most durable BI lessons come straight from software engineering. This is the clearest example.
The hidden cost of definitions-in-dashboards
When a metric's definition lives inside a chart, three things happen, and all of them are bad.
Drift is the default. Someone builds a churn chart with one definition. Six months later, someone else builds a churn chart for a different team with a slightly different window. Neither knows the other exists. Now "churn" means two things, and nobody decided that on purpose. Multiply across every metric and every team and you get an organization that cannot agree on its own performance.
Changes do not propagate. Finance decides refunds should be excluded from revenue. In a definitions-in-dashboards world, that is a scavenger hunt: find every chart, open each one, hope you did not miss any. With a semantic layer, you edit net_revenue once and every Insight that references it is correct on the next run.
Nothing is testable. You cannot write a test against a SUM() that only exists as a string buried in a chart config. You can absolutely write a test against a named metric, and once you can test it, you can put it in CI and catch a broken definition before it reaches a stakeholder, which is the whole premise of testing before you deploy a dashboard.
A metric is code, so treat it like code
The reframe that makes this click: a metric definition is code. Not metadata, not configuration trivia. It is code, with all the rights and responsibilities that implies.
Code gets reviewed. When net_revenue changes, that change shows up in a pull request, a human approves it, and the diff is permanently in your Git history. Six months later when someone asks "why did Q2 revenue restate?", the answer is one git blame away. This is the BI-as-code thesis applied at the metric grain. It is the same argument we make for whole dashboards, pushed down to the smallest meaningful unit.
Code gets reused. A metric defined once on the orders model is available to every Insight, every dashboard, and every teammate. Relations let metrics compose across models without anyone re-writing a join:
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}"
Code gets tested. And code has a single source of truth, which is the entire point.
"But the BI tool already has measures"
Most BI tools do have some notion of a shared measure. The difference is where it lives and who can touch it. In a GUI tool, the measure lives in a proprietary metadata store, edited through a UI, owned by whoever has admin access. It is shared, but it is not code. You cannot diff it, you cannot test it in CI, and you cannot review a change to it the way you review every other change your team ships. We have written about that tradeoff in detail in YAML vs. GUI BI configuration.
A code-native semantic layer puts the definition in a text file in your repository, next to everything else your team versions. That is the unlock: the metric is governed by the exact same workflow (branch, PR, review, merge, CI) that governs the rest of your engineering org. No new process to invent. No separate place to look.
Where this is going
The payoff compounds. Once every metric has one trustworthy definition, the layer above it gets dramatically more powerful. AI-assisted analysis, for instance, is only as good as the definitions it reasons over. Point a model at a pile of ad-hoc dashboard SQL and you get confident nonsense; point it at a clean semantic layer and it has a real map of your business. A trustworthy semantic layer is the prerequisite for everything interesting that comes next.
This is why we made it the foundation of Visivo 2.0: Insights build directly on the semantic layer, so a metric you define once flows into every chart, filter, and split with no copy-pasting and no drift. If you want to feel the difference, the examples gallery is built entirely on this model, and /get-started takes you from install to your first shared metric in a few minutes.
The next time three dashboards disagree about revenue, do not schedule a meeting. Move the definition somewhere they all have to read from.
Previously in Visivo
This is the thinking behind Visivo 2.0, where Insights and the semantic layer became the single way to build charts. For the broader case that BI should live in your codebase, start with what BI-as-code is and why it matters.