Are you considering dbt for Microsoft Fabric, or already using it? A question worth asking is whether it covers the whole of what you'll build on Fabric.

dbt brought real engineering discipline to SQL development: dependency management, testing, documentation and repeatable deployments. If your Fabric work is mostly SQL transformations in a Warehouse, dbt may be all you need.

Fabric is broader than that. Python, Spark, Lakehouse, Warehouse, OneLake and Power BI share one platform, and as a project uses more of it, the places where dbt stops start to matter.

A typical project grows piece by piece. A Python notebook for ingestion. A Spark job for Lakehouse processing. A dbt project for the Fabric Warehouse. Fabric pipelines to connect them. Separate arrangements for deployment, testing and monitoring. Each addition is reasonable on its own. But they add up, and at some point you start to wonder: are we designing our data engineering around what Fabric makes possible, or around what our framework was designed to support?

This article walks through six places where those pieces meet, and what to check about each before you commit.

The first seam: a table is a table, in Python, Spark SQL or T-SQL

A production table needs the same things whether Python, Spark SQL or T-SQL populates it: staging, primary keys, change detection, merging, auditing, and the ability to resume safely after a failure. These aren't SQL features. They're data engineering requirements.

Fabric lets you pick the right engine for each job. Python for acquisition and awkward file formats. T-SQL for relational modelling. Spark SQL for scale.

dbt gives SQL models that discipline. It supports Python models on some platforms, but not on Fabric: Microsoft's Lakehouse adapter covers SQL models only, with no Python support planned. Tables loaded by Python sit outside dbt, so the engineering standard often changes when the language does: different development patterns, different execution, different conventions.

A primary key doesn't stop being a primary key because you wrote it in Python. So why should choosing Python mean stepping outside your engineering framework? And why should a data team have to invent another one to fill the gap? The programming language changes how a table is implemented. It doesn't change the standard the table is held to.

The second seam: files are important too

The advantage of Microsoft Fabric is that it can handle files as well as tables. Published workbooks, PDFs, HTML pages, CSV extracts and API responses land in a Lakehouse's Files area, beside the tables built from them. A lot of the data that matters arrives this way, and acquiring it is often the slowest and most fragile part of the pipeline.

From an engineering point of view, files raise the same questions as tables:

  • Change detection. Which files are new, which changed and which were withdrawn since the last run. A publisher correcting a file at the same URL is a change; the same file downloaded twice is not, so content matters more than timestamps.
  • Dependencies. Which tables are built from which files, so a changed file reloads only the tables that read it.
  • Metadata. Where each file came from, when it was retrieved and what it contained, carried through to the rows built from it.
  • Incremental load. Acquisition fetches only what is missing or changed, and each table reads only the files that changed since its own last successful load. Files that can't be acquired again are history, and need protecting from a rebuild.

dbt starts from sources that are already tables. Its source freshness check looks at the latest load timestamp in a table; it has no record of which files arrived, changed or disappeared. Acquisition, and the record of what was acquired, happens before dbt, usually in notebooks and pipelines with a manifest of the team's own design.

Why should the files a pipeline depends on be tracked less carefully than the tables built from them?

The third seam: orchestrating jobs, not data, across Lakehouse and Warehouse

Take the common pattern: a notebook populates Delta tables, a dbt job transforms them, another job builds the final models, and a pipeline connects the stages. Microsoft documents patterns like this, and they work.

dbt understands the dependencies between its own models, and handles them well. On Fabric it can build in both places: dbt-fabric for the Warehouse in T-SQL, and dbt-fabricspark for the Lakehouse in Spark SQL. But each dbt run targets one adapter, so Lakehouse and Warehouse models usually sit in separate projects or runs, and references between projects are a dbt platform Enterprise feature. dbt still doesn't see the Python that feeds them, so the orchestration that spans them sits in a layer above, scheduling notebooks and jobs.

Now suppose one source changes, affecting two Delta tables and three Warehouse tables. The real dependencies are between those five objects. So why are we scheduling, rerunning and monitoring the notebooks and jobs that contain them? As the estate grows, those job boundaries start to decide how failures, concurrency and reruns are handled.

Fabric brings Lakehouse and Warehouse together on one platform. The dependency graph can span them too, rather than stopping at the boundary between them.

The fourth seam: keeping track of what's changing

Running a warehouse in production is more than executing transformations. Things change all the time, and the warehouse should know what changed, where, and what it affects.

Suppose a source delivers 100,000 records and only five have changed. The warehouse should update those five, leave the rest untouched, and record exactly what happened.

Now suppose 40,000 records vanish overnight. Were they deleted, or did the extract break?

Or a definition changes, and the tests that passed yesterday no longer describe today's data. Or a load fails halfway. Where should the next run start?

These are basic warehouse disciplines: detecting genuine change, handling deletes deliberately, keeping change timestamps that mean something, knowing what each table has already read, and advancing bookmarks only when a load succeeds.

dbt covers parts of this. Incremental models process new rows one model at a time. Snapshots keep the history of a table's rows, with valid-from and valid-to dates and options for recording hard deletes. That answers "what did this row look like last March?". It doesn't answer "what did last night's run change?": there are no per-run counts of inserts, updates and deletes, no record of how far each consumer has read, and nothing for files or Python loads.

So teams build their own bookkeeping around dbt. Then again for Python. And again for files in the Lakehouse.

Why should keeping track of a data warehouse be a do-it-yourself project through customisation?

The fifth seam: OneLake changes how you can develop

OneLake is one of my favourite things about Fabric.

Some of my government sources take hours to acquire. With OneLake shortcuts, development can reuse that source data without copying or downloading it again. Change a transformation, rebuild the affected objects, inspect, repeat. Dozens of iterations in the time one acquisition would take.

But shortcuts alone don't give you that workflow. The framework has to know which data is borrowed, which objects it manages locally, and how to deploy changes safely.

dbt's closest feature is defer: when a developer runs only the models they changed, references to the rest read the production tables instead. That saves rebuilding upstream SQL models. It isn't a development copy of the estate, though. It covers only dbt's own models, not the files and Python loads before them, which are usually the slowest data to acquire again.

Does the engineering framework take advantage of the innovation Microsoft has put into Fabric, or does it limit how much of it the team can adopt?

The sixth seam: who understands the whole Fabric estate?

By now there are many moving parts: notebooks, Lakehouse tables, Warehouse models, pipelines, separate execution histories, tests and deployment configurations.

When something breaks, where does the team look? Which object failed? What sits downstream of it? What actually changed? Which tests are now stale? Can just the affected work be rerun?

dbt has lineage and test results. Fabric has monitoring. A shared Git repository and a master pipeline help. Each shows part of the picture.

This matters more as AI agents take on development work. An agent learns through a tight loop: make a change, run it, read the result, fix it. From a command line, that loop takes seconds.

Split a pipeline across notebooks and dbt, and the loop breaks at every seam. The dbt half runs from a terminal, and dbt Labs' MCP server gives an agent access to it. The notebook half runs somewhere else, with its results somewhere else again. The agent can change the code, but it struggles to see what its change did.

What to look for in a Fabric data engineering framework

What if we designed data engineering around Fabric, rather than fitting Fabric around a SQL framework? Whether you use dbt on its own, build around it, or choose something else, these are the capabilities worth checking against the work you'll do on Fabric:

  • One standard for every table, whichever engine populates it.
  • Files tracked as carefully as tables: what arrived, what changed, what was withdrawn.
  • Dependencies between objects, not jobs, crossing Lakehouse and Warehouse.
  • A record of what changed in every run, and how far each consumer has read.
  • Loading, testing, auditing and recovery under one set of conventions.
  • Development that reuses data through OneLake rather than copying it.
  • One operational record of what was deployed, loaded and tested.

The question is less about dbt itself than about what a data engineering framework should be responsible for on a multi-engine platform.

Putting it into practice: one framework across Fabric

This is the approach we took with Weaver, an open-source data engineering framework designed around Fabric's native capabilities. It's one way to meet the checklist above:

  • One standard for every table. Python, Spark SQL and Warehouse T-SQL objects share one load contract: staging, declared keys, change detection, explicit deletes, audit columns and data-quality safeguards. Write a Warehouse SELECT and Weaver creates the table and loading procedure around it.
  • Files as managed objects. A Folder records every file it acquires, changes or removes, with per-run counts. Each Table reading it asks for the files added, changed or deleted since its own bookmark, and Folders holding history that can't be acquired again can be protected from a rebuild.
  • One dependency graph across Fabric. Python files, Lakehouse Delta tables, Warehouse tables, semantic models and Reports sit in a single graph. A load runs in dependency order, with Spark and each Warehouse in their own concurrency lanes, and a failure blocks only the work that depends on it.
  • Change tracking built in. Each table keeps its own bookmark, which advances only when its load succeeds, and every run records the rows inserted, updated and deleted. When a definition changes, the tests that depend on it are marked stale.
  • Development on existing data. Mirror sets up a development workspace over existing data through OneLake shortcuts and Warehouse views, and materialises only the objects you change.
  • One operational record. Deployments, loads and tests are recorded in Fabric Warehouse tables. Health shows what is failing or stale, and a Power BI dashboard covers the whole estate.
  • Native Power BI. Deploy PBIP and TMDL semantic models and Reports, apply shared rules, and unit test DAX measures against SQL results.

Build, load and test run from a command line on your desktop, or from a Fabric notebook after pip install weaverstack. The same commands give an AI agent the full loop of change, run, check and fix across the whole pipeline, without leaving the terminal.

We built Weaver after years of enterprise data warehousing on SQL Server. We didn't want to give up that engineering discipline, or run separate frameworks for different parts of Fabric.

The benefit isn't just fewer pipelines. It's using Fabric's full capability without solving the same engineering problems again and again. See all of Weaver's features.

Deciding whether dbt is enough

dbt on its own is likely a good fit when:

  • most of your work is SQL transformations in a Fabric Warehouse
  • ingestion is handled elsewhere and rarely changes
  • you already have a working dbt project and a team that knows it

But if your work is spreading across engines, it's worth asking some harder questions.

How much effort goes into coordinating separate systems?

How often does the team adapt its engineering to fit the framework?

Which Fabric capabilities is the team avoiding because they're awkward to integrate?

The tooling is moving. As at November 2026, Microsoft's dbt job runtime runs dbt Core 1.11 with both Fabric adapters, and dbt Labs has said its newer Fusion engine will come to Fabric. Check what is current before deciding.

There's no reason to abandon an architecture that works. The useful question is how much of your Fabric work dbt covers, and how much you'll build and run beside it.

See how Weaver compares in practice: Weaver for dbt developers.