Business Health Dashboard Leaders Actually Open
A practitioner’s plan to build a business health dashboard that leadership actually uses—metrics, piping, dbt models, BI choices, weekly ritual, and failure modes.
Your leadership wants a single view of what matters, not another dashboard they’ll stop opening in two weeks. Here’s the playbook we use to ship a business health dashboard that sticks: choose 8–12 metrics, define them once, wire Stripe/QuickBooks/HubSpot/GA with reliable jobs, model with dbt so numbers reconcile, pick a BI that fits your team, and run a short weekly review so decisions actually change. You can build this in-house with the patterns below, or bring us in to accelerate the parts that bite in production.
The smallest useful dashboard: 8–12 metrics that answer “Are we on track?”
Pick fewer metrics than you think. If a dashboard requires a narrator, it won’t be used. Start with one page, one time range (last 7/28/90 days), and clear deltas vs target.
- Revenue: MRR/ARR and recognized revenue. Track forecast vs actual to expose timing issues.
- Gross margin: revenue less COGS as a %.
- Retention: logo and dollar retention (D30/D90), plus churn reason mix.
- Pipeline coverage: next 90 days pipeline ÷ target, by owner/segment.
- Win rate and sales cycle length.
- Activation or product adoption: a leading indicator tied to value (e.g., first value event).
- Support health: first response time, backlog age, SLA breach rate.
- Cash and burn runway if relevant to financial health.
- Invoice collections: AR aging buckets; failed payment recovery.
Make every tile answer one question and show one decision. Add a note on “how to act.” Use small, consistent labels: “Revenue (MRR)”, “Retention D90”. Don’t bury the key metrics behind filters. If you must add more, split them into a secondary page later.
Define metrics once: a dictionary your BI and Slack can both use
Disagreement on definitions kills trust. Write a metrics dictionary before you write SQL. Keep one file in the repo with names, owners, grain, filters, and authoritative SQL references. Treat it as code: PRs, reviews, and change logs.
# metrics.yml (repo root)
version: 1
metrics:
- name: revenue_mrr
owner: finance
grain: month
definition_sql: ref('fct_billing_mrr')
filters:
- active_subscription = true
freshness: 24h
notes: Monthly recurring revenue from Stripe invoices; excludes refunds.
- name: retention_d90
owner: revops
grain: day
definition_sql: ref('fct_retention_cohort')
calc: retained_users_90d / cohort_users
- name: pipeline_coverage
owner: sales_ops
grain: week
calc: sum(pipeline_next_90d) / sum(target_next_90d)
- name: gross_margin_pct
owner: finance
grain: month
calc: (revenue - cogs) / revenue
Reference models in the dictionary, not raw tables. Add a simple validator in CI that checks every metric resolves to a model and compiles. Use short canonical names in BI and in your weekly agenda. If you later add derived metrics (e.g., net dollar retention), append, don’t redefine.
# lightweight validation (Python)
import yaml, subprocess, sys
m = yaml.safe_load(open('metrics.yml'))
errors = []
for metric in m['metrics']:
if 'definition_sql' in metric:
try:
subprocess.check_call(['dbt', 'compile', '--select', metric['definition_sql']])
except subprocess.CalledProcessError:
errors.append(metric['name'])
if errors:
print('Broken metric refs:', errors)
sys.exit(1)
If you want help templating this practice across teams, see our Business Health Reporting service.
Wire your systems: Stripe, QuickBooks, HubSpot, GA4—reliably
Connectors are easy to start, hard to keep clean. Choose managed pipelines (Fivetran, Airbyte Cloud) for speed, or run open-source where cost/control matter. Lean on webhooks for change events, not just nightly pulls. Document the data sources once, with owners and SLAs.
- Stripe: fetch charges, invoices, subscriptions, and balance transactions. Reconcile fees and disputes. Beware timezone drift in created vs period_end.
- QuickBooks: invoices and payments lag; set a cutoff hour so yesterday’s books don’t flip under you.
- HubSpot: lifecycle stage history comes as denormalized JSON—flatten it consistently.
- GA4: sampling and attribution windows change; store the export in Parquet so you can reprocess.
# example: incremental Stripe pull to S3 (Python)
import os, time, stripe, json, boto3
stripe.api_key = os.environ['STRIPE_API_KEY']
s3 = boto3.client('s3')
start = int(os.environ.get('SINCE_TS', 0))
for obj in stripe.Charge.list(created={"gte": start}, limit=100):
key = f"stripe/charges/ts={obj.created}/{obj.id}.json"
s3.put_object(Bucket=os.environ['BUCKET'], Key=key, Body=json.dumps(obj))
time.sleep(0.05) # Stripe rate limits aggressively
Partition storage by date and id to support idempotent loads. Land to bronze, normalize to silver, then model gold tables in dbt. Keep a small backfill script you can run per-table when a bug is fixed. Version your extractor container images so a library update doesn’t silently change payloads.
Standing up the warehouse or lakehouse? Our guide on the modern data stack for startups covers the minimum that works.
Model with dbt so the dashboard stays fast and trusted
Dashboards die when numbers change underfoot or loads run too slow. Model facts and dimensions with stable keys, then aggregate for the dashboard. Use incremental models and partition pruning. Document tests in schema YAML so breaks fail the build, not the meeting.
-- models/fct_billing_mrr.sql
{{ config(materialized='incremental', unique_key='invoice_id', on_schema_change='sync_all_columns') }}
with invoices as (
select *
from {{ source('stripe', 'invoices') }}
{% if is_incremental() %}
where updated_at > (select coalesce(max(updated_at), '1970-01-01') from {{ this }})
{% endif %}
)
select
invoice_id,
customer_id,
period_start::date as period_start,
period_end::date as period_end,
amount_due/100.0 as amount_due,
refunded/100.0 as refunded,
(amount_due - refunded)/100.0 as mrr_net,
updated_at
from invoices
where status in ('paid','open');
# models/schema.yml (tests)
version: 2
models:
- name: fct_billing_mrr
columns:
- name: invoice_id
tests: [not_null, unique]
- name: mrr_net
tests:
- not_null
- dbt_utils.expression_is_true:
expression: ":= mrr_net >= 0"
For a 40M-row orders table on an X-Small warehouse, push heavy joins into staging and pre-aggregate by day. Cache volatile metrics in a small aggregate table the dashboard hits directly. If your dbt runs creep, our dbt repo performance work and this primer on dbt materializations will save hours and credits. If you see slow tiles, profile queries first; it’s usually filters on non-partitioned columns.
Pick a BI that fits your team, not a demo
Choose the dashboard surface by who will own it six months from now. Evaluate governance, ad hoc speed, and how easy it is to keep the one-page view stable. A good dashboard provides clarity without needing 15 filters.
| Team profile | Tool | Strengths | Watch-outs |
|---|---|---|---|
| SQL-first, governed explores | Looker/Looker Studio | Modeled semantics; row-level security | Modeling upfront; iterate slower |
| Self-serve ops; sheets power users | Sigma | Spreadsheet UX on warehouse | Easy to over-join; enforce semantic layer |
| Mixed PM/Eng; notebooks welcome | Hex | Notebooks + apps; rapid prototyping | Governance needs care |
| Microsoft stack | Power BI | Deep Office integration | Desktop authoring quirks |
| Lightweight teams | Metabase | Fast to value; pulses | Keep SQL sources disciplined |
Whatever you pick, keep the business intelligence layer thin. Lock the homepage to a single page with targets. Use labels from your metrics dictionary verbatim so governance is consistent. Add a “Source” link on each tile back to the dbt model or doc. Reserve separate spaces for experiments so the homepage stays clean. Include a small “key performance” legend explaining targets and colors.
Make it stick: the weekly review ritual
Great dashboards change behavior. Book a 30-minute slot. Agenda: 3-minute scan, 10-minute deep dive on misses, 10-minute owner updates, 5-minute decisions captured as tasks. No slide decks allowed—only the dashboard.
- Assign owners per metric. If a tile is red two weeks running, it gets an action with a due date.
- Send a Monday Slack digest with deltas and links. Keep it visible.
- Let a Slack-native agent answer “why” by linking to the right drill view, not guessing.
# Slack digest (Python)
import os, requests
def post(msg):
requests.post(os.environ['SLACK_WEBHOOK'], json={"text": msg})
msg = """
Business Health — W28
Revenue (MRR): $1.23M (+2.1% WoW)
Retention D90: 92.4% (-0.6pp) — owner @alex
Pipeline Coverage: 2.9x (target 3.5x) — review opp source mix
"""
post(msg)
Automate the digest from the warehouse so you aren’t hand-copying numbers. If a handful of metrics must be near real-time, isolate them and load them more often; the rest can stay daily. This keeps the workflow predictable and makes the meeting about decisions, not hunting numbers. If you want to uplevel facilitation and hands-on practice, our Training & Enablement programs cover runbooks and review habits that last.
Why dashboards get abandoned (and how to avoid it)
Common failure modes we see after week two:
- Too many tiles. A dashboard turning into a catalog dilutes attention. Keep the homepage to 8–12 and spin up drill pages.
- Names drift. Marketing “Revenue” vs Finance “Revenue.” The dictionary prevents this. Treat changes like code changes.
- Stale or slow. Tiles timed out? Pre-aggregate hot paths. Load critical tables first. You don’t need everything to be real-time.
- Unowned numbers. Assign owners and keep a changelog so an executive trusts what moved and why.
- Unclear thresholds. Color means nothing without a target and timeframe.
- Blind spots. Only outcome metrics, no leading indicator. Add one or two that predict movement.
Recover trust with alerts when a metric definition changes and a short “what changed” note in the dashboard description. Show lineage from raw data to tile so a stakeholder can trace issues. Improve operational efficiency by pushing root-cause links into tickets. When tradeoffs arise, ship the stable one-page view over fancy interactions—business dashboards earn attention by being boring and correct. If you need a second set of eyes on repo shape or slow queries, we wrote dbt run slow? Why it happens and 7 fixes that work.
Implementation details that save hours later
Small choices up front keep the dashboard fast and reliable.
- Time zones: pick one warehouse timezone for modeling; convert in BI at render time only if necessary.
- IDs: store Stripe, HubSpot, and QuickBooks IDs as strings; avoid lossy numeric casts.
- Late-arriving data: design incremental models to re-scan the last N days; store a watermark per table.
- Backfills: build a toggle per model to force a full-refresh by date, not table-wide.
- Access: separate “dashboard” reader roles from “explorer” roles so edits don’t break tiles during meetings.
- Targets: store targets in a small table by period/segment so the dashboard can chart deltas cleanly.
-- targets table
create table if not exists analytics.targets (
metric_name varchar,
period_date date,
segment varchar,
target_value numeric
);
Dashboards help when they’re predictable: same layout every week, same owners, same definitions. If you change anything, call it out on the page in a one-line changelog. Keep the homepage query set tiny and push deep dives to linked explores. When you add a new tile, delete an old one.
FAQ: types, healthcare context, and integrated views
What are the four types of dashboards? Strategic (board-level), operational dashboards (front-line cadence), analytical (exploratory), and tactical/KPI (owner-level execution). What is a health dashboard? In healthcare, it’s a governed view aggregating clinical, financial, and operational signals to guide health management. What does a KPI dashboard look like? One page with key performance indicators, owners, targets, and trend spark lines. What is the CMS dashboard? Typically Centers for Medicare & Medicaid Services reporting tiles tracking measures and compliance deadlines. How do integrated dashboards improve decision-making? They join systems so teams make informed decisions without reconciling exports.
What are healthcare dashboards used for? A healthcare organization monitors patient satisfaction, patient care, population health, and care quality across health systems. They often join the electronic health record with claims and staffing to improve patient outcomes. Who uses them? Healthcare professionals and healthcare executives for planning and daily ops. Want more dashboard examples? Start with your own data and sketch business dashboard examples you can actually maintain; copy only patterns you can explain in one sentence. What are the benefits of using healthcare dashboards? Visibility, compliance readiness, and faster coordination across electronic health data.
Key features of a business dashboard? Clear ownership, targets, filters that match language in sales/support, and fast tiles. Integrated feeds from CRM, billing, and product logs. A single glossary of performance indicators. Ready to see AI-augmented surfaces elevate decision-making? Our take on stickier agent patterns is here: AI for Data Teams: Workflows That Actually Stick.
If you want this built right the first time—or want a review before launch—start with a quick scoping call: Start a project with Vertex.
About the author
Eric Provencio — Analytics engineer who has built and run production data platforms for Disney, Hulu, Nike, Peloton, Gopuff, and Kaplan. Founded Vertex Data Consulting to do the deep work most data teams never find time for: dbt Cloud migrations, repo performance, Airflow reliability, and AI agents that actually touch the stack.