If sales, finance, and product each report a different revenue number, the problem is rarely the warehouse. It is that 'revenue' was never defined in one place, so every team computed it their own way.
The symptoms show up in meetings: time spent arguing about whose number is right instead of deciding what to do. That tax is invisible on any budget line, but it is real, and it compounds as the company grows.
Fixing it is a sequence: agree the definitions, model the data once, test it like software, and then get out of the way.
Definitions before dashboards
A trustworthy data platform starts with agreed definitions. Until the answers to questions like these live in one written place, no amount of tooling will make the numbers agree:
- What counts as an active user, over what window, and does an API-only integration count?
- Does revenue include or exclude refunds, discounts, tax, and unbilled usage? Is it recognised or booked?
- When does a trial become a customer, and when does a churned customer stop counting?
- What timezone and what date, event time or processing time, anchors a daily metric?
The semantic layer
Once the definitions are written down, encode each one exactly once. Modern tooling calls this a semantic layer or metrics layer: a single place where 'monthly recurring revenue' is defined as a formula over modeled tables, and every dashboard, notebook, and export reads that definition rather than reimplementing it.
The practical effect is that changing how a metric is calculated is a one-line change in one file, reviewed like code, and it propagates everywhere. Nobody can quietly ship a dashboard with a subtly different formula, because there is no formula to redefine.
The pipeline is plumbing; the definitions are the product.
Model once, reuse everywhere
Raw event data is messy and inconsistent. Modeling transforms it into clean, well-named tables that map to how the business actually thinks: one row per order, per user per day, per subscription.
The pattern that holds up is layered: a staging layer that lightly cleans and renames each source, a core layer of conformed entities and facts, and a marts layer shaped for specific consumers. Each layer is materialized and documented. Downstream, every team builds from the core layer, so a metric means the same thing no matter who pulls it.
That discipline is what 'a single source of truth' really means in practice: not one database, but one modeled definition of each concept.
Test it like code
Data breaks silently. A source changes a field, a pipeline half-fails, and a dashboard shows a confident but wrong number that nobody questions until a decision has already been made on it.
Automated tests and checks catch these before they reach a human. The ones worth having:
- Schema and not-null tests on every source, so an upstream change fails loudly instead of nulling a column.
- Uniqueness and referential tests on keys, so a fan-out join does not silently double a metric.
- Freshness checks: alert if a table has not updated within its expected window.
- Row-count and distribution anomaly checks: alert if today's volume is 50 percent off the trailing average.
- Reconciliation tests: the modeled revenue total must match finance's system of record within a tolerance.
Data contracts with the source teams
Most silent breakage originates in the application, where a developer renames a field or changes an enum without knowing a pipeline depends on it. A data contract makes that dependency explicit: the source team commits to a schema and a set of guarantees, and changes go through a review that includes the data team.
This does not need heavy tooling to start. A documented schema, a test in the source repo's CI that fails on a breaking change, and a shared channel for coordination covers most of the value.
The goal is to move breakage from 'discovered in a board meeting' to 'caught in a pull request'.
Make it self-serve
The final step is getting out of the way. A clean modeled layer, a semantic layer of trusted metrics, and good documentation let teams answer their own questions without filing a ticket and waiting days.
Decisions speed up because nobody is relitigating whose spreadsheet is right, and the data team is freed from being a human query service to work on the model, the tests, and the genuinely hard analysis.
You know it is working when the phrase 'let me check which number is correct' stops coming up in meetings.
What bad data actually costs
The cost is rarely a single dramatic failure. It is a slow tax: an analyst spends a day reconciling two reports, a launch decision is delayed a week for 'one more check', a board deck gets rebuilt twice, a pricing change is made on a number that turns out to be double-counted.
None of it shows up on a budget line, which is exactly why it persists. A useful exercise is to ask three teams how many hours a week they spend getting to a number they trust. The answer is usually a shock, and it makes the investment in the modeled layer easy to justify.
The modeled layer is not a data-team project. It is infrastructure for every decision the company makes.
Ownership and documentation
A metric without an owner drifts. Assign each core metric and each modeled table a named owner who is responsible for its definition and its tests. When someone wants to change how 'active user' is calculated, there is a person to convince and a review to go through.
Documentation lives next to the model, not in a separate wiki that rots. Every table gets a one-line description per column and a note on its grain (one row per what). Every metric gets its definition in plain language alongside the formula.
The test of good documentation: a new analyst can find and correctly use the revenue metric without asking anyone.
Key takeaways
- Write metric definitions down and agree them before building any dashboard.
- Encode each metric once in a semantic layer that every tool reads from.
- Model raw data in layers: staging, conformed core, consumer marts, all documented.
- Add schema, uniqueness, freshness, anomaly, and reconciliation tests to the pipeline.
- Use lightweight data contracts so source changes are caught in review, not production.
- Design for self-serve so teams answer their own questions and the data team builds the model.