Est.

Linking BI Dashboard Queries to dbt Model Versions

dbt versioning prevents dashboard breakage when data models change, but only inside dbt itself.

· 10 min read · Updated
Cover illustration for “Linking BI Dashboard Queries to dbt Model Versions”
SQL Traceability · September 20, 2026 · 10 min read · 2,291 words

A dbt model, once another team or another system depends on it, functions like an API rather than just a SQL file. Producers want room to change the model. Consumers need a guarantee that their queries won't break without warning. That tension isn't a side effect of bad planning, it's built into the arrangement itself, because producers need freedom to change the model while consumers need to trust it won't break without warning, and these incentives are in direct conflict.

The conflict appears in how BI dashboards consume warehouse relations. A dashboard queries a named table or view in the warehouse. It has no idea a dbt project produced that table, no visibility into the project's structure, and no way to know when something upstream shifts. So when a column gets removed, renamed, or its type changes, the dashboard doesn't get a warning. It returns the wrong numbers, or fails.

The people hit hardest are usually the last to find out. Product managers, sales ops, and operations teams don't look at the dbt DAG. They have no way to anticipate a schema change because nothing in their daily tools tells them one is coming. By the time someone notices the quarterly revenue chart looks wrong, the damage to trust is already done.

Software API design settled this problem decades ago: version the thing. Maintaining an old endpoint costs something, but an unplanned migration is far costlier, a principle reflected in dbt's model versioning docs. The rest of this piece is about how dbt tries to apply that same logic to data models, where it succeeds, and where the analogy runs out of road.

What Constitutes a Breaking Change

Calling a dbt model an API only means something if there's a precise definition of what breaks it. dbt handles this through model contracts, explicit guarantees about a model's structure, and a selector called state:modified that checks whether a proposed change violates one.

state:modified watches for five categories of change: removing a column, changing a column's data type, removing or modifying constraints, removing contract enforcement entirely, and deleting or renaming a contracted model. Trip any of these, and the selector flags the change as breaking.

Plenty of changes don't count. Adding a new column, fixing a calculation without touching its type, adding new constraints, these pass cleanly, and the model's contract stays valid with no version bump required. That distinction decides how a team routes the work: a breaking change needs a new model version, while a non-breaking one just needs the contract YAML updated alongside the SQL. Picture a model called mrt__analytics__customers, with a contract YAML listing its columns and types. Rename one of those columns, and the contract no longer matches reality. That mismatch is what state:modified is built to catch before it reaches a downstream dashboard.

The categories sound clean until a real example tests them. Take a column defined as numeric, discovered to be silently rounding values in a way nobody wants. The fix is changing it to numeric([38,2](https://adriennevermorel.com/notes/dbt-model-versioning/)). Technically, that's a type change, so the selector flags it as breaking, even though the intent behind the change is purely corrective. Nobody downstream is hurt by more precision. But dbt's detector doesn't know "corrective" from "destructive," it only knows "type changed." That gap between what the rule catches and what actually deserves scrutiny becomes important later.

How model versioning gives producers a controlled migration window

Once a change is flagged as breaking, the question becomes what a producer does about it without forcing every consumer to update at the same moment. dbt's answer, introduced in dbt 1.5, is model versioning: producers can materialize multiple versions of a model at once, so nobody downstream faces a sudden cutover.

Each version becomes its own physical object in the warehouse. mrt__analytics__customers_v1 and mrt__analytics__customers_v2 can sit side by side, each with its own enforced contract. A ref() call that doesn't specify a version resolves to whatever is marked latest_version. Anyone still depending on the old shape pins it explicitly: ref('mrt__analytics__customers', v=1). Nothing forces that consumer to touch their query until they're ready.

For tools that can't use ref() at all, dbt adds a latest_version_pointer: a view named after the model's base name that always resolves to whatever is currently latest, which protects external tools and BI dashboards that sit outside the dbt project entirely.

The deprecation side works the same way, on the producer's timeline rather than the consumer's. A deprecation_date gets set on the old version, right in its properties YAML. Once the date passes and consumers have moved over, the old version gets removed and the warehouse gets cleaned up.

dbt's coordination guide lays this out as a six-step producer workflow: decide whether the change actually needs a new version, create the new version safely by keeping latest_version pointed at the old one so nobody breaks by default, add a deprecation date to the old version, tell consumers about the new version and the timeline, promote the new version to latest_version once people have migrated, and clean up the deprecated version once it's no longer in use.

This matters even more in a dbt Mesh setup, where one team's project references another team's model through a cross-project ref(). The consuming team can pin to v1 and migrate whenever their own roadmap allows, while the producing team keeps shipping changes on their own schedule. Nobody's sprint planning depends on somebody else's sprint planning.

Diagram: dbt's Six-Step Producer Workflow for Breaking Changes. Visualizes: Illustrate the six sequential steps a producer follows when a breaking change requires a new model version in dbt.

The boundary between dbt and the BI tool

All of this machinery works beautifully for anything inside the dbt project. It stops working the moment a consumer sits outside dbt's view. dbt's version-safety contract ends at the warehouse relation name, it doesn't reach into a BI tool's query editor, and anything that isn't a dbt consumer has to be managed by hand.

Think about what that means for a BI dashboard. A tool like Power BI, or really any external system, has no concept of dbt's versioning contract, no awareness of deprecation warnings, and no visibility into migration timelines. If a business team isn't ready to move, nothing stops them from pointing their dashboard straight at dim_region_v1 in the warehouse, provided they even know that table exists and know to go looking for it. For a lot of teams, that's a big "provided."

Those deprecation warnings, the ones meant to give everyone a heads-up, show up in dbt build logs. Business users never open a build log. They open a BI tool. So the exact signal that's supposed to prevent surprise breakage lives in a place the people most at risk of surprise breakage never look.

And the risk doesn't run in only one direction. Microsoft's February 2026 Power BI update shipped four separate deprecation announcements, the kind of change most teams won't notice until something stops working. The data team can version its dbt models as carefully as it wants. The BI tool itself can still deprecate a feature a dashboard depends on, entirely outside the data team's control. Version safety on one side of the pipe doesn't guarantee version safety on the other.

The friction in dbt's breaking-change detection and the community's response

Even within dbt's own boundary, the breaking-change detector draws complaints, and the complaints are fair. The system flags plenty of changes as breaking that feel, in practice, like corrections rather than threats.

The numeric to numeric([38,2](https://adriennevermorel.com/notes/dbt-model-versioning/)) example from earlier is the one people keep bringing up. The intent is a fix that every consumer benefits from immediately. The state:modified selector doesn't see intent, it sees a type change, so it demands a new version for something that was never meant to break anyone. That mismatch creates real overhead: a full versioning cycle, deprecation dates, communication to consumers, for a change that was supposed to make things better, not different.

As of early 2026, the community has floated a few fixes, and none of them have merged yet. One proposal is an --allow-breaking flag for CI, letting a team explicitly say "yes, we know, ship it anyway". Another is a breaking: false annotation that could sit right in the YAML, letting a producer override the detector's judgment on a case-by-case basis. A third direction aims lower in the plumbing: more granular detection that tells widening changes, the safe ones, apart from narrowing changes, the unsafe ones, instead of treating every type change the same way.

There's a second friction, separate from false positives. Because non-breaking additions pass through without any versioning overhead, they tend to pile up. A model's most important columns accumulate company over time, some used, some forgotten, some half-deprecated, until the schema itself gets harder to read and maintain. That's the trade a team makes for avoiding constant version bumps: fewer disruptions, messier tables.

For incremental models carrying a contract, on_schema_change: 'sync_all_columns' is a trap. It silently drops any column not present in the latest run, which is precisely the kind of breaking change a contract exists to prevent. The safer settings are 'append_new_columns', or 'append_new_columns' paired with 'fail', leaving any real schema migration to go through versioning deliberately rather than by accident.

None of this makes versioning the wrong discipline. It makes the tooling around it imperfect, which is a different problem, and a more fixable one. Recent developments on the tooling side are starting to close some of these detection and observability gaps, particularly on the side the BI tool boundary left wide open.

Dashboard Definitions in the Model's Git Branch

dbt Charts is the clearest attempt yet to pull dashboards inside the same version-control discipline that dbt already applies to models. A board, in dbt Charts, is a single YAML file, sitting in a charts/ directory right next to models/ in the dbt project. It supports sixteen chart types and can render to HTML, PDF, PNG, SVG, JSON, YAML, or straight to a terminal.

What makes this relevant to the versioning problem isn't the chart types but where the file lives. A chart definition checked into the same repo as the model it queries can be checked against that model before anything ships. The dct validate command compares a chart's column references against what the model actually outputs, and if a chart points at a column the model no longer produces, the pull request fails right there. The dct impact command goes a step further, showing which boards read a given column before anyone renames it, so a data team can see the downstream blast radius before making the change rather than after a dashboard breaks.

Wiring that into CI with dbt parse && dct validate catches a missing column at pull-request time. Compare that to the old failure mode: a business user opening a dashboard Monday morning and finding broken numbers, with no idea why. One of those is a red X on a PR.

The MotherDuck integration, shipped in September 2026, makes the idea concrete. Dashboard definitions deploy via dbt build straight to MotherDuck Dives, in what the documentation describes as two config changes. Those Dives refresh live whenever a stakeholder actually opens them, so the pipeline and the visualization function as one artifact instead of two systems bolted together.

That said, dbt Charts has a real gap at launch. Charts currently reference raw queries, not governed metrics, because dbt Charts is disconnected from the dbt Semantic Layer. That means a metric can be defined one way in the Semantic Layer and a slightly different way inside a chart's raw query, and nothing today stops those two definitions from drifting apart. Bringing dashboards into Git catches schema breakage before it ships. It doesn't, by itself, solve the problem of two sources of truth disagreeing with each other.

The dbt Semantic Layer as the governance layer non-technical teams interact with

Which is where the Semantic Layer earns its place in this story. If model versioning protects the relation, and dbt Charts protects the dashboard's connection to that relation, the Semantic Layer protects the thing most non-technical users actually touch: the metric itself.

Centralizing metric definitions means a business user querying "monthly active customers" is querying a named, governed metric. Product, sales, marketing, and ops teams pull pre-defined metrics through whatever BI tool or natural-language interface they already use, without writing SQL or knowing the warehouse schema underneath. A model version bump that happens underneath the Semantic Layer doesn't automatically ripple out to break anyone's dashboard, because the dashboard was never touching the model directly in the first place.

Once a producer updates the metric definition to point at the new model version, every consumer of that metric sees the update at once, without editing a single dashboard. This is the mechanism that makes self-serve analytics actually workable at scale: fewer ad hoc requests landing in the data team's queue, and more confidence that whatever a business user sees is consistent with what everyone else sees.

The surrounding ecosystem has been moving in the same direction. Semantic Layer querying in dbt Insights reached general availability in January 2026, letting users build SQL queries against the Semantic Layer without writing SQL by hand. MetricFlow itself was open-sourced under Apache 2.0 in October 2025, aligned with the Open Semantic Interchange initiative alongside Snowflake and Salesforce, a push toward metric definitions that travel across vendors rather than getting locked into one. And at Coalesce 2025, Bilt Rewards connected its semantic layer and underlying warehouse to an LLM, pointing toward a future where most people, technical or not, actually ask questions of governed metrics rather than raw schema.

dbt Charts pulls dashboard definitions into the same repo and the same CI checks as the models feeding them. The Semantic Layer sits above both, giving non-technical teams a stable, governed surface to query so that the churn underneath never has to become their problem.

Sources

  1. 2025 dbt platform release notes | dbt Developer Hub
  2. dbt release notes | dbt Developer Hub
  3. docs.getdbt.com
  4. dbt Model Versions: Breaking-Change Management (2026)
  5. docs.getdbt.com
  6. docs.getdbt.com
  7. docs.getdbt.com
  8. github.com
Filed underSQL Traceability

More in SQL Traceability