Paths Subjects Questions Quizzes Pricing Search
Intermediate Open Free

Diagnosing a Dashboard That Broke Days After the Actual Cause

Your company's executive monthly_recurring_revenue dashboard, built on top of a dbt model, is fed by a daily pipeline that pulls subscription data from a third-party billing provider's API. On Tuesday, the billing provider ships an unannounced API change: a plan_price field that used to be an integer in cents (4900 for $49.00) becomes a decimal in dollars (49.00). The field's name and type are unchanged, so it still passes every existing schema check and NOT NULL constraint. Your transformation logic, which divides the raw value by 100 to convert cents to dollars, keeps running without error — it just now produces numbers that are 100x too small. Nobody notices until Friday, when a sales leader flags in Slack that "the MRR chart looks broken."

  1. Explain precisely why none of your existing checks (schema validation, NOT NULL, unique) caught this, tying your answer to the specific category of schema change involved.
  2. Design the monitoring that would have caught this within a day instead of three, and explain mechanistically why each check you propose would have fired.
  3. Walk through your incident response once the sales leader's message comes in: what do you do first, second, third, and why does the order matter?
  4. Propose a longer-term fix to the relationship with the billing provider itself, and explain why a purely technical fix (better monitoring) isn't sufficient on its own.
Solution

1. Why every existing check passed

This is a semantic schema change with no structural signature: the field kept its name (plan_price), kept its type (numeric), and remained non-null on every row. A schema validator checks structural properties — field presence, name, type, nullability — and every one of those properties is unchanged. A NOT NULL constraint has nothing to object to, since the field is still populated. unique isn't even relevant to this field. The actual break is in meaning: the number now represents a different unit (dollars instead of cents), and no mechanical, structure-based check can detect a meaning change that leaves every structural property intact. This is precisely the category of break that requires either an explicit value-level contract (e.g., a documented, tested assumption about the field's unit or expected range) or a check that has memory of the field's historical values to compare against — which is exactly what part 2 builds.

2. Monitoring that would have caught it within a day

A distribution monitor on plan_price — tracking the mean, min, and max per daily load, compared against a rolling historical baseline — would have fired on Wednesday's very first load: a field whose average value drops by roughly 100x overnight is an obvious outlier against any reasonable historical band, even a simple standard-deviation threshold. This works precisely because it doesn't require anyone to have anticipated "the billing provider might change units" in advance; it only requires noticing that a numeric column's aggregate shape moved sharply, which is what distribution monitoring is built to do independent of the specific cause. As a second, complementary check: a dbt reconciliation test comparing SUM(monthly_recurring_revenue) computed in the warehouse against an independent total pulled directly from the billing provider's own reporting endpoint, run on every build, would fail the build itself before the dashboard ever refreshes with the bad number — catching it even earlier, at the pipeline layer, rather than after the data is already exposed to a dashboard.

3. Incident response walkthrough

First, triage: before touching anything, confirm the scope — query when the drop actually started (Wednesday's load, not a gradual decline, which points to a discrete upstream change rather than slow drift) and trace lineage to see what else depends on plan_price. In this case, only the MRR model is affected; other dashboards built on the same source table but not referencing price (signup counts, churn) are untouched, so the blast radius is one metric, not the whole table. Second, containment: mark the MRR dashboard as stale with a visible banner rather than leaving a confidently wrong (99%-lower) number up where an executive might report it externally, and pause the specific model consuming plan_price so Friday and Saturday don't compound three more days of bad data on top of the three already affected. Only third, root-cause: diff a captured API payload from before and after the change to confirm the exact mechanism (cents-to-dollars unit change). The order matters because root-causing first, before containment, would mean spending time investigating while the dashboard keeps showing a wrong number that people might act on — stopping the damage takes priority over understanding it.

4. Longer-term fix and why monitoring alone isn't enough

Open a conversation with the billing provider about a versioned API contract with a deprecation-notice process for breaking field changes — the underlying problem is that an externally-owned producer can change the meaning of a field with zero warning, and no amount of monitoring on your side prevents that from happening again on some other field next quarter. Monitoring (part 2) is necessary but not sufficient on its own because it's reactive — it shortens detection time from days to hours, but it doesn't prevent the underlying risk, which is a producer relationship with no contract. The systemic fix addresses the category of risk (any field from this producer could silently change meaning); the monitoring fix addresses the speed of detecting the next instance of it. A mature postmortem does both, and explicitly separates "what we fixed" from "what we prevented" so the team doesn't mistake a faster detection mechanism for having removed the risk.

Share this question

← Back to Data Quality, Testing & Observability practice

We use cookies for product analytics to improve OmniAtlas. See our Privacy Policy.