BI Lead Journey: Product-Led Growth Analytics Platform
Persona: Emily Park, BI Lead at CloudFlow Goal: Build the company's analytics infrastructure for product-led growth, providing canonical SaaS metric definitions, self-service analytics for product managers, and board-ready reporting -- all from a single source of truth.
Primary Workbenches: BI Workbench, Data Workbench Supporting Services: Ingestion Service, Catalog Service, Query Engine, Semantic Layer, Pipeline Service, Data Quality Service, Governance Service
Business Context
CloudFlow's analytics are fragmented across five tools: Stripe dashboards for revenue, Amplitude for product analytics, Salesforce reports for pipeline, Google Sheets for board decks, and ad-hoc SQL queries for everything else. The result: the finance team reports ARR of 20.4M, and the CEO's board deck says $19.8M. Nobody agrees on what "churn" means -- is it logo count, revenue, or usage-based?
Emily's mandate: build a single analytics platform where every team uses the same metric definitions, every dashboard pulls from the same governed data, and the board deck is generated automatically -- not manually assembled from screenshots.
Current State Target State
┌────────────────────────┐ ┌────────────────────────┐
│ 5 analytics tools │ │ 1 governed platform │
│ 3 different ARR numbers│ │ 1 canonical ARR number │
│ Manual board decks │ │ Auto-generated reports │
│ │ │ │
│ PM self-service: 0% │ ────▶ │ PM self-service: 80% │
│ Deck prep: 3 days │ │ Deck prep: 15 minutes │
└────────────────────────┘ └────────────────────────┘Stage 1: Ingestion
Emily reviews the data sources needed for the PLG analytics platform. Most are already connected by the data engineering team; she configures additional sources for cost analytics and support metrics.
Source Inventory
| Source | Connector | Sync Mode | Frequency | Owner |
|---|---|---|---|---|
| Product PostgreSQL | Airbyte PostgreSQL CDC | Incremental (WAL) | Every 15 min | Data Eng |
| Segment Events (Kafka) | Airbyte Kafka | Streaming | Continuous | Data Eng |
| Stripe Billing | Airbyte Stripe | Incremental | Hourly | Emily (new) |
| Salesforce CRM | Airbyte Salesforce | Incremental | Every 30 min | Data Eng |
| AWS Cost Explorer | Airbyte AWS Cost Explorer | Full refresh | Daily | Emily (new) |
| Zendesk Support | Airbyte Zendesk | Incremental | Every 15 min | Data Eng |
Stripe Webhook for Real-Time MRR
Emily configures a Stripe webhook integration for real-time subscription events (upgrades, downgrades, cancellations), enabling live MRR tracking:
{
"source": "stripe_webhooks",
"connector": "airbyte/source-stripe",
"config": {
"streams": [
"subscriptions", "invoices", "charges",
"customers", "plans", "products"
],
"sync_mode": "incremental",
"cursor_field": "created",
"frequency": "every_hour",
"webhook_events": [
"customer.subscription.created",
"customer.subscription.updated",
"customer.subscription.deleted",
"invoice.paid",
"invoice.payment_failed"
]
}
}AWS Cost Explorer Connection
{
"source": "aws_cost_explorer",
"connector": "airbyte/source-aws-cost-explorer",
"config": {
"streams": ["cost_and_usage"],
"granularity": "DAILY",
"group_by": ["SERVICE", "REGION", "USAGE_TYPE"],
"sync_mode": "full_refresh",
"frequency": "daily_at_0600_utc",
"filter": {
"not": {
"dimensions": {
"key": "RECORD_TYPE",
"values": ["Credit", "Refund"]
}
}
}
}
}Stage 2: Discovery
Emily explores the data catalog to map the SaaS metrics data model, identify data quality issues, and tag sensitive data for SOC2 compliance.
Data Quality Discovery
Emily profiles the billing data and discovers a critical issue: timezone mismatches between Stripe (UTC) and the product database (America/Los_Angeles for historical reasons):
-- Detect timezone mismatches between Stripe and product DB
SELECT
s.subscription_id,
s.created AS stripe_created_utc,
w.created_at AS product_created_local,
-- If product DB stores Pacific time, there is a gap
s.created - w.created_at AS time_difference
FROM stripe.subscriptions s
JOIN postgresql.workspaces w
ON s.metadata->>'workspace_id' = CAST(w.workspace_id AS VARCHAR)
WHERE ABS(EXTRACT(EPOCH FROM (s.created - w.created_at))) > 28800
LIMIT 10;Finding: 12% of subscription records have timestamp misalignments causing off-by-one errors in daily MRR calculations. Emily adds a timezone normalization step to the metrics pipeline.
SOC2 Compliance Tagging
Emily tags sensitive columns in the data catalog for governance policy enforcement:
{
"governance_tags": [
{
"table": "users",
"column": "email",
"classification": "PII",
"masking_policy": "hash_sha256",
"applies_to_roles": ["analyst", "pm"],
"exempt_roles": ["admin", "compliance"]
},
{
"table": "stripe.customers",
"column": "payment_method",
"classification": "PCI",
"masking_policy": "redact",
"applies_to_roles": ["ALL"],
"exempt_roles": ["finance_admin"]
},
{
"table": "subscriptions",
"column": "mrr",
"classification": "FINANCIAL",
"masking_policy": "none",
"access_audit": true
}
]
}Metric Lineage Mapping
Emily traces the lineage of the "ARR" metric to understand why three teams calculate it differently:
Finance team (Stripe dashboard):
invoices.amount_paid → SUM(last 12 months) → manual annualization
Sales team (Salesforce):
opportunities.amount → SUM(closed-won, current year)
Product team (internal dashboard):
subscriptions.mrr → SUM(active) × 12
Emily's canonical definition:
subscriptions.mrr (active status, exclude trials)
→ SUM by month → × 12
→ validated against Stripe invoices ± 2%Stage 3: Query
Emily defines the canonical SaaS metrics in the Semantic Layer, building the single source of truth for all analytics downstream.
Semantic Layer Definitions
-- SEMANTIC LAYER: Canonical SaaS Metric Definitions
-- These are registered in the BI Workbench and available to all users
-- ============================================================
-- METRIC: Monthly Recurring Revenue (MRR)
-- Owner: Emily Park | Validated against: Stripe Dashboard
-- ============================================================
-- Definition: Sum of monthly recurring subscription amounts for
-- active subscriptions, excluding trials and one-time charges.
SELECT
DATE_TRUNC('month', s.current_period_start AT TIME ZONE 'UTC')
AS month,
SUM(CASE
WHEN s.status = 'active' AND s.trial_end < CURRENT_TIMESTAMP
THEN s.mrr
ELSE 0
END) AS mrr,
COUNT(DISTINCT CASE
WHEN s.status = 'active' AND s.trial_end < CURRENT_TIMESTAMP
THEN s.workspace_id
END) AS paying_workspaces
FROM subscriptions s
GROUP BY 1;
-- ============================================================
-- METRIC: Net Dollar Retention (NDR) - Trailing 12 Months
-- Formula: (Starting MRR + Expansion - Contraction - Churn) / Starting MRR
-- ============================================================
WITH monthly_mrr AS (
SELECT
workspace_id,
DATE_TRUNC('month', current_period_start AT TIME ZONE 'UTC') AS month,
SUM(mrr) AS mrr
FROM subscriptions
WHERE status = 'active'
AND trial_end < CURRENT_TIMESTAMP
GROUP BY 1, 2
),
mrr_changes AS (
SELECT
COALESCE(curr.workspace_id, prev.workspace_id) AS workspace_id,
curr.month,
COALESCE(curr.mrr, 0) AS current_mrr,
COALESCE(prev.mrr, 0) AS previous_mrr,
CASE
WHEN prev.mrr IS NULL THEN 'new'
WHEN curr.mrr IS NULL THEN 'churned'
WHEN curr.mrr > prev.mrr THEN 'expansion'
WHEN curr.mrr < prev.mrr THEN 'contraction'
ELSE 'retained'
END AS change_type
FROM monthly_mrr curr
FULL OUTER JOIN monthly_mrr prev
ON curr.workspace_id = prev.workspace_id
AND curr.month = prev.month + INTERVAL '1' MONTH
)
SELECT
month,
SUM(CASE WHEN change_type = 'new' THEN current_mrr ELSE 0 END) AS new_mrr,
SUM(CASE WHEN change_type = 'expansion'
THEN current_mrr - previous_mrr ELSE 0 END) AS expansion_mrr,
SUM(CASE WHEN change_type = 'contraction'
THEN previous_mrr - current_mrr ELSE 0 END) AS contraction_mrr,
SUM(CASE WHEN change_type = 'churned' THEN previous_mrr ELSE 0 END) AS churned_mrr,
-- NDR = (retained + expansion) / starting MRR
ROUND((SUM(CASE WHEN change_type IN ('retained', 'expansion')
THEN current_mrr ELSE 0 END) * 1.0) /
NULLIF(SUM(CASE WHEN change_type != 'new'
THEN previous_mrr ELSE 0 END), 0), 3) AS ndr
FROM mrr_changes
GROUP BY month
ORDER BY month;
-- ============================================================
-- METRIC: Churn Rate (Logo and Revenue)
-- ============================================================
SELECT
DATE_TRUNC('month', cancelled_at AT TIME ZONE 'UTC') AS month,
-- Logo churn: workspaces cancelled / starting workspaces
COUNT(DISTINCT CASE WHEN status = 'cancelled'
THEN workspace_id END) * 1.0 /
NULLIF(COUNT(DISTINCT workspace_id), 0) AS logo_churn_rate,
-- Revenue churn: MRR lost / starting MRR
SUM(CASE WHEN status = 'cancelled' THEN mrr ELSE 0 END) /
NULLIF(SUM(mrr), 0) AS revenue_churn_rate
FROM subscriptions
GROUP BY 1;Cohort Analysis
Emily builds reusable cohort analysis queries for the product team:
-- Signup cohort retention analysis (monthly cohorts)
WITH cohorts AS (
SELECT
workspace_id,
DATE_TRUNC('month', created_at AT TIME ZONE 'UTC') AS cohort_month
FROM workspaces
WHERE plan != 'free'
),
monthly_activity AS (
SELECT
e.workspace_id,
DATE_TRUNC('month', e.timestamp) AS activity_month
FROM events e
WHERE e.event_type NOT IN ('session_start', 'session_end', 'heartbeat')
GROUP BY 1, 2
)
SELECT
c.cohort_month,
DATE_DIFF('month', c.cohort_month, a.activity_month) AS months_since_signup,
COUNT(DISTINCT a.workspace_id) AS active_workspaces,
COUNT(DISTINCT c.workspace_id) AS cohort_size,
ROUND(COUNT(DISTINCT a.workspace_id) * 100.0 /
COUNT(DISTINCT c.workspace_id), 1) AS retention_pct
FROM cohorts c
LEFT JOIN monthly_activity a ON c.workspace_id = a.workspace_id
AND a.activity_month >= c.cohort_month
GROUP BY c.cohort_month, DATE_DIFF('month', c.cohort_month, a.activity_month)
ORDER BY c.cohort_month, months_since_signup;Validation Against Finance
Emily validates her MRR calculations against the finance team's Stripe-exported numbers:
-- Cross-validate MRR: Semantic Layer vs Stripe raw invoices
WITH semantic_mrr AS (
SELECT month, mrr AS semantic_mrr
FROM semantic_layer.mrr_monthly
),
stripe_mrr AS (
SELECT
DATE_TRUNC('month', i.period_start AT TIME ZONE 'UTC') AS month,
SUM(i.amount_paid / 100.0) AS stripe_mrr
FROM stripe.invoices i
WHERE i.status = 'paid'
AND i.billing_reason = 'subscription_cycle'
GROUP BY 1
)
SELECT
s.month,
s.semantic_mrr,
st.stripe_mrr,
ROUND(ABS(s.semantic_mrr - st.stripe_mrr), 2) AS difference,
ROUND(ABS(s.semantic_mrr - st.stripe_mrr) * 100.0
/ st.stripe_mrr, 2) AS pct_difference
FROM semantic_mrr s
JOIN stripe_mrr st ON s.month = st.month
ORDER BY s.month DESC;| Month | Semantic MRR | Stripe MRR | Difference | % Diff |
|---|---|---|---|---|
| 2026-02 | $1,718,400 | $1,722,100 | $3,700 | 0.21% |
| 2026-01 | $1,694,200 | $1,697,800 | $3,600 | 0.21% |
| 2025-12 | $1,681,900 | $1,686,100 | $4,200 | 0.25% |
Difference is within the 2% tolerance (caused by proration timing). Finance team signs off on the semantic layer definitions.
Stage 4: Orchestration
Emily builds the metrics computation pipeline that powers all downstream analytics.
Metrics Pipeline
{
"pipeline": {
"name": "saas-metrics-daily",
"schedule": "0 7 * * *",
"description": "Daily SaaS metrics computation, cohort analysis, and board data refresh",
"stages": [
{
"name": "normalize_timezones",
"type": "sql_transform",
"description": "Convert all timestamps to UTC, fix product DB Pacific time",
"query_ref": "timezone_normalization.sql",
"output_table": "analytics.events_utc"
},
{
"name": "compute_mrr",
"type": "sql_transform",
"depends_on": ["normalize_timezones"],
"query_ref": "mrr_monthly.sql",
"output_table": "analytics.mrr_monthly"
},
{
"name": "compute_ndr",
"type": "sql_transform",
"depends_on": ["compute_mrr"],
"query_ref": "ndr_calculation.sql",
"output_table": "analytics.ndr_monthly"
},
{
"name": "compute_cohorts",
"type": "sql_transform",
"depends_on": ["normalize_timezones"],
"query_ref": "cohort_retention.sql",
"output_table": "analytics.cohort_retention"
},
{
"name": "compute_infrastructure_costs",
"type": "sql_transform",
"depends_on": ["normalize_timezones"],
"query_ref": "infra_cost_per_user.sql",
"output_table": "analytics.infrastructure_cost_daily"
},
{
"name": "quality_gate",
"type": "data_quality",
"depends_on": ["compute_mrr", "compute_ndr", "compute_cohorts"],
"checks": [
{
"name": "mrr_continuity",
"type": "custom_sql",
"query": "SELECT ABS(curr_mrr - prev_mrr) / prev_mrr FROM analytics.mrr_monthly ORDER BY month DESC LIMIT 1",
"max_value": 0.10,
"severity": "critical",
"note": "MRR should not change more than 10% month-over-month"
},
{
"name": "stripe_reconciliation",
"type": "custom_sql",
"query": "SELECT ABS(semantic_mrr - stripe_mrr) / stripe_mrr FROM analytics.mrr_reconciliation ORDER BY month DESC LIMIT 1",
"max_value": 0.02,
"severity": "warning"
},
{
"name": "cohort_completeness",
"type": "row_count",
"table": "analytics.cohort_retention",
"min_rows": 100,
"severity": "critical"
}
]
},
{
"name": "refresh_board_deck",
"type": "sql_transform",
"depends_on": ["quality_gate"],
"query_ref": "board_deck_metrics.sql",
"output_table": "analytics.board_deck_latest",
"trigger_dashboard_refresh": true
}
]
}
}Weekly and Monthly Pipelines
{
"pipelines": [
{
"name": "saas-metrics-weekly",
"schedule": "0 8 * * MON",
"stages": [
{
"name": "weekly_cohort_analysis",
"type": "sql_transform",
"query_ref": "weekly_cohort_deep_dive.sql"
},
{
"name": "feature_adoption_report",
"type": "sql_transform",
"query_ref": "feature_adoption_weekly.sql"
}
]
},
{
"name": "board-report-monthly",
"schedule": "0 9 1 * *",
"stages": [
{
"name": "generate_board_metrics",
"type": "sql_transform",
"query_ref": "board_metrics_monthly.sql"
},
{
"name": "validate_against_finance",
"type": "data_quality",
"checks": [
{
"name": "arr_matches_finance",
"type": "custom_sql",
"query": "SELECT ABS(our_arr - finance_arr) / finance_arr FROM analytics.arr_reconciliation",
"max_value": 0.005,
"severity": "critical",
"note": "Board-reported ARR must match finance within 0.5%"
}
]
}
]
}
]
}Stage 5: Analysis
Emily validates the SaaS metrics against board-reported numbers and identifies actionable insights for the executive team.
Segment Analysis: SMB vs Enterprise
Emily discovers that SMB and Enterprise segments behave very differently, warranting separate tracking:
-- Segment-level SaaS metrics
SELECT
CASE
WHEN s.mrr >= 5000 THEN 'Enterprise'
WHEN s.mrr >= 1000 THEN 'Mid-Market'
WHEN s.mrr >= 200 THEN 'SMB'
ELSE 'Self-Serve'
END AS segment,
COUNT(DISTINCT w.workspace_id) AS accounts,
ROUND(SUM(s.mrr), 0) AS total_mrr,
ROUND(AVG(s.mrr), 0) AS avg_mrr,
ROUND(AVG(activity.dau), 0) AS avg_dau,
ROUND(AVG(activity.features_adopted), 1) AS avg_features,
ROUND(AVG(CASE WHEN w.status = 'cancelled' THEN 1.0
ELSE 0 END) * 100, 1) AS churn_rate_pct,
ROUND(AVG(scores.churn_probability), 2) AS avg_churn_risk
FROM workspaces w
JOIN subscriptions s ON w.workspace_id = s.workspace_id AND s.status = 'active'
LEFT JOIN analytics.workspace_activity activity ON w.workspace_id = activity.workspace_id
LEFT JOIN ml.churn_scores_daily scores ON w.workspace_id = scores.workspace_id
GROUP BY 1
ORDER BY total_mrr DESC;| Segment | Accounts | MRR | Avg MRR | Avg DAU | Features | Churn % | Churn Risk |
|---|---|---|---|---|---|---|---|
| Enterprise | 120 | $840K | $7,000 | 142 | 7.2 | 11.7% | 0.18 |
| Mid-Market | 480 | $528K | $1,100 | 28 | 4.8 | 15.0% | 0.31 |
| SMB | 1,800 | $270K | $150 | 6 | 3.1 | 18.0% | 0.42 |
| Self-Serve | 1,800 | $82K | $46 | 2 | 1.8 | 19.0% | 0.55 |
Identifying Expansion Revenue Signals
-- Leading indicators of expansion (upgrade/seat addition)
WITH expansion_events AS (
SELECT
workspace_id,
DATE_TRUNC('month', event_date) AS expansion_month,
expansion_type,
mrr_increase
FROM analytics.subscription_changes
WHERE expansion_type IN ('plan_upgrade', 'seat_addition')
),
pre_expansion_signals AS (
SELECT
e.workspace_id,
e.expansion_month,
-- Usage signals 30 days before expansion
AVG(a.seat_utilization) AS avg_seat_util,
AVG(a.dau) AS avg_dau,
AVG(a.features_adopted) AS avg_features,
MAX(a.hit_seat_limit) AS hit_seat_limit,
MAX(a.hit_storage_limit) AS hit_storage_limit,
COUNT(DISTINCT t.ticket_id) AS support_tickets
FROM expansion_events e
JOIN analytics.workspace_activity_daily a
ON e.workspace_id = a.workspace_id
AND a.activity_date BETWEEN e.expansion_month - INTERVAL '30' DAY
AND e.expansion_month
LEFT JOIN support_tickets t
ON e.workspace_id = t.workspace_id
AND t.subject LIKE '%limit%' OR t.subject LIKE '%upgrade%'
GROUP BY e.workspace_id, e.expansion_month
)
SELECT
ROUND(AVG(avg_seat_util), 2) AS avg_seat_utilization,
ROUND(AVG(avg_features), 1) AS avg_features_adopted,
ROUND(AVG(CASE WHEN hit_seat_limit = 1 THEN 1.0 ELSE 0 END), 2)
AS pct_hit_seat_limit,
ROUND(AVG(CASE WHEN hit_storage_limit = 1 THEN 1.0 ELSE 0 END), 2)
AS pct_hit_storage_limit
FROM pre_expansion_signals;Key finding: 78% of expansions are preceded by hitting the seat limit within 30 days. Accounts using > 5 features expand at 3x the rate of those using < 3 features. This directly validates Raj's feature recommendation engine investment.
Stage 6: Productionization
Emily builds the dashboard suite and enables self-service analytics for product managers.
Dashboard Suite
Emily designs six dashboards in the BI Workbench:
CloudFlow Dashboard Suite
┌────────────────────────────────────────────────────────┐
│ │
│ 1. SaaS Metrics Overview (Executive) │
│ MRR, ARR, NDR, Churn, ARPA -- real-time │
│ │
│ 2. Product Usage (Product Team) │
│ DAU/MAU, Feature Adoption, Activation Funnel │
│ │
│ 3. Funnel Analytics (Growth Team) │
│ Signup → Activation → Conversion → Expansion │
│ │
│ 4. Customer Health (Customer Success) │
│ Churn scores, Health trends, CSM workload │
│ │
│ 5. Infrastructure Costs (Engineering) │
│ Cost per user, Cost per service, Trend analysis │
│ │
│ 6. Board Report (CEO/Board) │
│ Auto-generated, finance-validated, exportable │
│ │
└────────────────────────────────────────────────────────┘SaaS Metrics Overview Dashboard
The primary executive dashboard shows real-time SaaS health:
| Section | Metrics | Refresh | Source |
|---|---|---|---|
| Revenue | MRR, ARR, New MRR, Expansion MRR, Churned MRR | Hourly (Stripe) | analytics.mrr_monthly |
| Retention | NDR (trailing 12m), Logo Churn, Revenue Churn | Daily | analytics.ndr_monthly |
| Growth | DAU, MAU, DAU/MAU ratio, Signups | Hourly (events) | analytics.product_metrics_daily |
| Unit Economics | CAC, LTV, LTV:CAC, Gross Margin | Weekly | analytics.unit_economics_weekly |
| Cohort Heatmap | Monthly cohort retention curves | Daily | analytics.cohort_retention |
Self-Service for Product Managers
Emily enables PM self-service with guardrails -- PMs can query product data but not billing or PII:
{
"self_service_config": {
"role": "product_manager",
"allowed_schemas": ["analytics", "product_events"],
"denied_schemas": ["stripe", "salesforce"],
"denied_columns": ["email", "payment_method", "ip_address"],
"query_limits": {
"max_scan_rows": 100000000,
"max_runtime_seconds": 120,
"max_concurrent_queries": 3
},
"pre_built_templates": [
{
"name": "Feature Adoption by Cohort",
"description": "How quickly each signup cohort adopts a given feature",
"parameters": ["feature_name", "date_range"]
},
{
"name": "Funnel Drop-off Analysis",
"description": "Identify where users drop off in a multi-step flow",
"parameters": ["funnel_steps", "date_range", "segment"]
},
{
"name": "A/B Test Results",
"description": "Compare metrics between experiment variants",
"parameters": ["experiment_id", "metric"]
}
]
}
}Stage 7: Feedback
Emily sets up alerting, data quality reports, and dashboard performance monitoring.
Alert Configuration
{
"alerts": [
{
"name": "mrr_drop_alert",
"metric": "mrr_monthly",
"condition": "month_over_month_change < -0.05",
"severity": "critical",
"notify": ["michael@cloudflow.io", "emily@cloudflow.io", "slack://exec-team"],
"message": "MRR dropped more than 5% month-over-month. Current: ${current_mrr}, Previous: ${previous_mrr}"
},
{
"name": "churn_spike_alert",
"metric": "weekly_churn_rate",
"condition": "value > 0.015",
"severity": "high",
"notify": ["slack://cs-team", "emily@cloudflow.io"],
"message": "Weekly churn rate exceeded 1.5%. ${churned_count} accounts cancelled."
},
{
"name": "infra_cost_per_user_alert",
"metric": "infrastructure_cost_per_user",
"condition": "month_over_month_change > 0.10",
"severity": "warning",
"notify": ["slack://eng-leads"],
"message": "Infrastructure cost per user increased > 10%. Current: $${current_value}/user/month."
},
{
"name": "stripe_reconciliation_alert",
"metric": "mrr_reconciliation_diff_pct",
"condition": "value > 0.02",
"severity": "critical",
"notify": ["emily@cloudflow.io", "finance@cloudflow.io"],
"message": "MRR calculation diverges from Stripe by more than 2%. Investigate timezone normalization."
}
]
}Weekly Data Quality Report
Emily generates a weekly data quality report tracking coverage and freshness:
-- Weekly data quality summary
SELECT
source_name,
MAX(last_sync_at) AS last_sync,
DATE_DIFF('minute', MAX(last_sync_at), CURRENT_TIMESTAMP)
AS minutes_since_sync,
COUNT(DISTINCT sync_date) AS days_with_data_7d,
ROUND(AVG(row_count), 0) AS avg_daily_rows,
SUM(CASE WHEN sync_status = 'failed' THEN 1 ELSE 0 END)
AS failed_syncs_7d,
ROUND(AVG(null_rate), 3) AS avg_null_rate
FROM analytics.ingestion_health
WHERE sync_date >= CURRENT_DATE - INTERVAL '7' DAY
GROUP BY source_name
ORDER BY minutes_since_sync DESC;Dashboard Performance Monitoring
Emily tracks dashboard load times to ensure sub-5-second performance:
| Dashboard | P50 Load | P95 Load | Daily Views | Status |
|---|---|---|---|---|
| SaaS Metrics Overview | 1.8s | 3.2s | 142 | OK |
| Product Usage | 2.1s | 4.8s | 89 | OK |
| Funnel Analytics | 3.4s | 7.1s | 34 | WARNING |
| Customer Health | 1.5s | 2.9s | 67 | OK |
| Infrastructure Costs | 2.8s | 5.2s | 23 | OK |
| Board Report | 4.2s | 8.9s | 12 | WARNING |
The Funnel Analytics and Board Report dashboards exceed the 5-second P95 target. Emily optimizes them by materializing the heaviest subqueries into the metrics pipeline.
Stage 8: Experimentation
Emily runs experiments to refine metric definitions, test new analytics capabilities, and measure the impact of self-service adoption.
Experiment 1: Revenue Churn Definition
Emily tests three definitions of "revenue churn" to determine which best aligns with how the board and investors think about the metric:
| Definition | Method | Result | Finance Alignment |
|---|---|---|---|
| Billing date | Churn counted on last invoice date | 24.8% | Matches Stripe exactly |
| Usage date | Churn counted when usage drops to zero | 21.2% | Captures abandonment before cancellation |
| Contract date | Churn counted on subscription end date | 25.1% | Matches CRM renewal tracking |
Decision: Primary reporting uses the billing date definition (matches investor expectations). A secondary "usage-based churn" metric is tracked internally to detect abandonment earlier (giving Zara's churn model more lead time).
Experiment 2: Embedded Analytics Pilot
Emily pilots embedded analytics for CloudFlow's customers -- giving them usage dashboards inside their own workspace:
{
"experiment": {
"name": "embedded-analytics-pilot",
"hypothesis": "Giving workspace admins a usage dashboard will increase engagement and reduce churn",
"variant": {
"name": "usage_dashboard",
"description": "In-workspace analytics showing team activity, project progress, and adoption metrics",
"target_accounts": 200,
"selection": "random_from_mid_market_segment"
},
"control": {
"description": "No embedded analytics (existing experience)",
"target_accounts": 200
},
"primary_metric": "90_day_retention",
"secondary_metrics": ["dau_change", "feature_adoption_change", "nps_change"],
"duration_weeks": 12
}
}Pilot results (week 12):
| Metric | Control | Treatment | Lift |
|---|---|---|---|
| 90-day retention | 84% | 91% | +8.3% |
| DAU per workspace | 6.2 | 7.8 | +25.8% |
| Features adopted | 3.4 | 4.1 | +20.6% |
| NPS | 38 | 46 | +21.1% |
Embedded analytics significantly improve all metrics. Emily recommends a full rollout as a premium feature for Business and Enterprise plans.
Measuring PM Self-Service Adoption
Emily tracks whether product managers are actually using the self-service analytics:
-- PM self-service adoption over time
SELECT
DATE_TRUNC('week', query_timestamp) AS week,
COUNT(DISTINCT user_id) AS unique_pms_querying,
COUNT(*) AS total_queries,
ROUND(AVG(query_duration_seconds), 1) AS avg_query_time,
COUNT(CASE WHEN used_template = true THEN 1 END) AS template_queries,
COUNT(CASE WHEN used_template = false THEN 1 END) AS custom_queries
FROM analytics.query_audit_log
WHERE user_role = 'product_manager'
AND query_timestamp >= CURRENT_DATE - INTERVAL '12' WEEK
GROUP BY 1
ORDER BY 1;| Week | Unique PMs | Queries | Avg Time | Template | Custom |
|---|---|---|---|---|---|
| Week 1 | 3 | 12 | 18.4s | 10 | 2 |
| Week 4 | 6 | 45 | 8.2s | 28 | 17 |
| Week 8 | 8 | 89 | 5.1s | 41 | 48 |
| Week 12 | 9 | 124 | 4.3s | 38 | 86 |
Self-service adoption is growing. By week 12, PMs are running more custom queries than template queries, indicating increasing SQL comfort. Emily's goal of 80% PM self-service is on track.
Summary
| Stage | Key Action | Platform Component | Outcome |
|---|---|---|---|
| 1. Ingestion | Connected Stripe (real-time webhooks), AWS Cost Explorer, verified 6 sources | Ingestion Service (Airbyte) | Hourly billing, daily costs, streaming events |
| 2. Discovery | Found timezone mismatches, tagged SOC2-sensitive data, mapped metric lineage | Data Workbench Catalog | Fixed 12% timestamp misalignment, governance tags applied |
| 3. Query | Defined canonical MRR, NDR, Churn in Semantic Layer; validated within 0.21% of Stripe | Semantic Layer + Query Engine | Single source of truth, finance sign-off |
| 4. Orchestration | Daily, weekly, monthly pipelines with quality gates and finance reconciliation | Pipeline Service (Temporal) | Automated metrics with < 2% variance guarantee |
| 5. Analysis | Segmented metrics (Enterprise 32% revenue churn), identified expansion signals | BI Workbench + Query Engine | Data-driven segment strategy, 78% expansion predictor |
| 6. Productionization | 6 dashboards, PM self-service with governance guardrails | BI Workbench + Governance | Executive, PM, CS, and Eng dashboards; self-service analytics |
| 7. Feedback | Alerts on MRR drop, churn spike, cost increase; weekly quality reports | BI Workbench Alerts | Automated monitoring, dashboard performance tracking |
| 8. Experimentation | Tested churn definitions, piloted embedded analytics (+8.3% retention), PM adoption | BI Workbench + Experiments | Embedded analytics validated, PM self-service growing |
Related Walkthroughs
- Data Scientist Journey -- Zara's churn scores appear in Emily's Customer Health dashboard
- ML Engineer Journey -- Raj's recommendation engine drives the feature adoption metrics Emily tracks
- Executive Journey -- Michael uses Emily's dashboards and board reports daily
- SaaS & Technology Overview -- Industry context and dataset reference