joshuavogel.dev
← All case studies

Mid-market E-commerce Platform · November 4, 2025

One Definition of Revenue: A Semantic Layer Finance Actually Signed Off On

Fourteen conflicting definitions of 'revenue' lived across Looker explores, dbt models, and spreadsheets. Board meetings opened with a 20-minute argument about whose number was right.

dbt MetricFlow Snowflake Looker 14 → 1 · Revenue definitions unified

Problem Statement

Revenue meant different things depending on who you asked: gross bookings, net of refunds, recognized vs. collected, pre- or post-discount. Fourteen variants were in active circulation. The data team’s real product — trust — was broken, and every new executive hire triggered a fresh “why don’t these numbers match?” fire drill.

System Architecture & Tradeoffs

Evaluated three approaches:

  1. Modeling-only (encode one blessed fct_revenue mart): cheapest, but logic still forks the moment someone needs a cut we didn’t pre-build.
  2. Headless BI (Cube): mature, powerful — but adds a JVM service, its own auth model, and a second transformation layer the team didn’t want to own.
  3. dbt Semantic Layer (MetricFlow): metric definitions live next to the models they reference, versioned in the same PRs.

We chose dbt Semantic Layer: one repo, one review process, and Looker connects via the dbt integration so dashboards query metrics, not tables.

Implementation Details

The work was 30% YAML, 70% negotiation. Before any code, I ran a two-week “metric treaty” process with Finance, RevOps, and Product to ratify definitions in writing. Only then did they get encoded:

semantic_models:
  - name: orders
    model: ref('fct_orders')
    defaults:
      agg_time_dimension: ordered_at
    entities:
      - name: order_id
        type: primary
    measures:
      - name: gross_revenue
        description: "Order amount USD, pre-refund, pre-discount"
        expr: amount_usd
        agg: sum
      - name: net_revenue
        description: "Gross minus refunds and discounts. THE board number."
        expr: amount_usd - refund_usd - discount_usd
        agg: sum

metrics:
  - name: net_revenue
    label: "Net Revenue (Certified)"
    type: simple
    type_params:
      measure: net_revenue

Change management was the real feature: every metric edit is a pull request requiring finance review, with dbt tests blocking deploys when a definition drifted from its certified value on a golden dataset.

Business ROI

14 → 1

Revenue definitions

Every surface queries the same certified metric

3 wks

To first certified metric

Treaty process included

0

Number-mismatch escalations

In the two quarters post-launch

Six months later, the CFO opened board prep by sharing her screen — of the dashboard. That had never happened before.

Facing something similar?

This is the shape of every engagement: fixed scope, senior-only execution, and a number you can put in a board deck at the end.