Why this matters
As a BI Developer, you translate business goals into numbers people trust. Without consistent KPI definitions, teams argue over whose number is right, dashboards conflict, and decisions stall. Your job is to define KPIs once, document them clearly, and implement them identically across reports and tools.
- Real tasks you will face: align Sales vs Finance revenue, standardize “Active User,” prevent double-counting in conversion rates, and maintain a single source of truth in the semantic layer.
- Outcomes: fewer meetings to reconcile numbers, faster dashboard delivery, and confident decision-making.
Concept explained simply
A KPI is a business question answered by a metric with precise rules. Consistency means every stakeholder gets the same answer when asking that question, regardless of tool or team.
Mental model
Think of a KPI as a contract:
- What: name and business question
- How: formula, filters, and grain
- Where: systems and tables used
- When: time grain, windows, and timezone
- Who: owner and approval authority
- Edges: exclusions, status changes, and tie-breakers
Reusable KPI spec template (copy/paste)
Name: [Clear metric name] Intent: [Business question this answers] Owner: [Role/team] Entities in scope: [users/accounts/orders/...] Formula: [mathematical expression] Numerator: [if rate] Denominator: [if rate] Filters: [included/excluded statuses] Time grain: [daily/weekly/monthly] Window & lag: [e.g., rolling 28d, 1-day delay] Timezone & calendar: [UTC, fiscal calendar rules] Currency: [if monetary], FX rule: [spot/average] Data sources: [semantic model tables/views] Edge cases: [refunds, test data, partial periods] Quality checks: [constraints and tests] Version: [vX.Y] Change log: [summary]
Step-by-step: establish consistent KPIs
- Inventory: list all existing KPI variants and where they appear (dashboards, slides, queries).
- Name and scope: choose one canonical name; list synonyms to map legacy terms.
- Define precisely: fill the KPI spec template. Be explicit about filters, grain, and edge cases.
- Centralize: implement the KPI in a governed semantic layer or shared views (one definition, many uses).
- Validate: reconcile with stakeholders; run unit tests and backfill checks.
- Publish: document in the catalog; mark legacy variants as deprecated and map to the canonical metric.
- Govern: version KPIs; log changes; communicate impacts.
Worked examples
Example 1 — Active Users (DAU)
Name: Daily Active Users (DAU)
Intent: How many unique users engaged today?
Entities in scope: users with a product event
Formula: count(distinct user_id)
Filters: include events with event_type in {login, session_start, purchase, feature_use}; exclude test_users=true
Time grain: daily (UTC), active if ≥1 qualifying event on day D
Edge cases: users with only email_open are not active
Data sources: analytics.events (semantic view)
Quality checks: DAU ≤ total users; DAU ≤ MAU; no sudden spikes >3x rolling median
Version: v1.0Example 2 — Revenue (Net)
Name: Net Revenue Intent: How much money we kept from sales in the period? Formula: sum(gross_amount) - sum(discounts) - sum(refunds) Filters: finalized=true; currency converted to USD using monthly average FX Time grain: monthly; calendar month Edge cases: chargebacks counted as refunds; tax excluded from gross_amount Data sources: finance.transactions_view Quality checks: Net ≤ Gross; no negative net across month Version: v1.1 (v1.0 used daily spot FX)
Example 3 — Conversion Rate (Signup → Purchase)
Name: Signup-to-Purchase Conversion Rate (28d) Formula: distinct_users_with_purchase_within_28d / distinct_signups Numerator: users with purchase_date ≤ signup_date + 28 days Denominator: distinct users with signup_date in period Filters: exclude employees and test accounts Time grain: cohort by signup month Edge cases: multiple purchases per user count once in numerator Quality checks: numerator ≤ denominator; rate between 0 and 1 Version: v2.0
Example 4 — Churn Rate (Logo, Monthly)
Name: Monthly Logo Churn Rate Formula: logos_lost_in_month / logos_at_start_of_month Filters: paying customers only; status_change="cancelled" Time grain: monthly; end-of-month status snapshot Edge cases: reactivations within same month count as non-churn Quality checks: churn ≤ 100%; beginning logos = prior month's ending logos Version: v1.3
Exercises
Do these in your notes or BI sandbox. Then compare with the solutions.
- Exercise 1 — Define a KPI
Create a full spec for “Active Subscribers (Monthly)” that counts unique paying subscribers who streamed ≥1 item in the month. Include exclusions for trials and staff accounts, and a 24-hour data lag. - Exercise 2 — Resolve a conflict
Sales reports show “Revenue” including discounts; Finance excludes discounts and uses monthly average FX. Propose a canonical definition, list synonyms, and a migration plan to deprecate the old metric while keeping historical comparability.
- Checklist:
- Clear name and intent
- Explicit formula and filters
- Defined grain, window, timezone/currency
- Edge cases documented
- Data sources and owner
- Quality checks
- Version and change log
Common mistakes and self-checks
- Ambiguous scope: “active” without event list. Self-check: list exact qualifying events.
- Mixed grains: numerator daily, denominator monthly. Self-check: confirm both at the same grain or cohort design.
- Hidden filters: excluding trials in one report but not another. Self-check: put all filters in the spec, not just in a dashboard.
- Unstated time rules: missing timezone or fiscal calendar. Self-check: specify UTC vs local and fiscal month rules.
- Silent changes: updating a KPI without versioning. Self-check: bump version and log the change.
Practical projects
- Catalog cleanup: pick 5 duplicated metrics across dashboards; consolidate into 2 canonical KPIs and deprecate the rest.
- Semantic layer uplift: implement 3 KPI measures in a shared model/view with unit tests.
- Audit dashboard: build a QA page showing KPI values across tools for yesterday/last 7 days/last 30 days with thresholds.
Who this is for
- BI Developers and Analytics Engineers building shared metrics
- Data Analysts who need consistent, trusted dashboards
- Product/Finance stakeholders aligning on definitions
Prerequisites
- Basic SQL (GROUP BY, DISTINCT, JOIN)
- Understanding of your core entities (users, orders, accounts)
- Familiarity with your BI tool’s semantic layer or data models
Learning path
- Draft KPI specs with the template
- Review with stakeholders and reconcile differences
- Implement in the semantic layer; add data tests
- Publish in the catalog and migrate dashboards
- Monitor and iterate with versioning
Next steps
- Finish the exercises and compare with the solutions below.
- Take the quick test to check your understanding.
- Pick one real KPI at work to standardize this week.
Mini challenge
In one paragraph, explain to an executive why two teams’ numbers differ for “monthly revenue” and how your canonical definition eliminates the discrepancy. Use non-technical language.
Exercise solutions
Exercise 1 — Suggested solution
Name: Monthly Active Subscribers (MAS)
Intent: How many paying subscribers engaged with content this month?
Owner: BI (Subscription Analytics)
Entities in scope: subscriber_id with active paid plan
Formula: count(distinct subscriber_id)
Filters: plan_status in {active, grace}; is_trial=false; is_staff=false; has_stream_event=true
Qualifying event: stream_started OR stream_completed
Time grain: monthly (UTC calendar month)
Window & lag: activity within month; 24-hour ingestion lag
Timezone & calendar: UTC; standard Gregorian calendar
Data sources: dwh.subscriptions, dwh.stream_events_view
Edge cases: partial month joins count if at least one qualifying event
Quality checks: MAS ≤ total paid subscribers; no >20% day-over-day jump without annotation
Version: v1.0
Change log: initialExercise 2 — Suggested solution
Canonical Name: Net Revenue (USD, monthly average FX) Synonyms mapped: "Revenue" (Sales), "Gross Sales" (legacy), "Finance Revenue" Formula: sum(gross_amount) - sum(discounts) - sum(refunds) [tax excluded] FX rule: convert to USD using monthly average FX rate for transaction month Time grain: monthly; posting_date Filters: finalized=true; exclude test and internal transactions Governance: set Net Revenue as canonical; mark Sales "Revenue" as deprecated with banner mapping Migration plan: (1) Implement canonical measure in semantic layer (REV_NET_USD_M) (2) Update top 10 dashboards; add dual-run comparison tile for 2 weeks (3) Freeze legacy measure; remove from new models (4) Communicate change log and impact to Sales/Finance QA tests: Net ≤ Gross; month-over-month variance within expected bounds unless annotated
Quick Test
The quick test is available to everyone; only logged-in users get saved progress.