DBT COMPATIBILITY
"100% backward compatible" is a strong claim, and here is the simple reason it holds: DVT does not reimplement dbt, fork dbt, or patch dbt — it runs your installed dbt-core and adds federation on top. If dbt does it, DVT does it, because it is dbt doing it.
TRY IT ON YOUR EXISTING PROJECT
The fastest way to believe the claim is to test it. Point DVT at a dbt project you already have:
pip install dvt-core cd your_existing_dbt_project dvt sync # installs adapters/drivers for your profiles.yml + compat macros dvt run # everything runs exactly as dbt ran it
Day one, nothing changes. Same models, same results, same output format — DVT prints dbt-identical START/OK/Done lines, so your tooling and habits keep working. Then, whenever you're ready, you add your first cross-engine source and your first f_table — without touching anything that already works.
THE WRAPPER ARCHITECTURE
Every dvt command wraps the corresponding dbt command. For compilation and standard models, DVT shells out to the dbt CLI and reads the artifacts dbt produces (manifest.json) — it never imports dbt internals. Two things follow from that:
- ▸Your dbt version can upgrade independently — DVT pins a range (dbt-core ≥1.9, <2.0), not a fork frozen in time.
- ▸There is no behavioral drift: a table model under dvt run IS dbt running that model, byte for byte.
WHAT WORKS UNCHANGED — BECAUSE IT IS DBT
| DBT FEATURE | STATUS IN DVT |
|---|---|
| ref(), source(), config(), var(), env_var() | Identical — rendered by dbt's own Jinja |
| Materializations: table, view, incremental, ephemeral | Identical — executed by the official adapter |
| Macros & packages (dbt Hub, private packages) | Identical — dbt resolves and renders them |
| Tests (generic + singular), store_failures | Identical — except tests on foreign-connection sources, skipped with a warning (dbt can't reach those relations) |
| Snapshots (timestamp & check strategies) | Identical — dvt snapshot = dbt snapshot |
| Selectors: --select / --exclude / tag: / graph operators | Identical — passed through to dbt |
| profiles.yml in ~/.dbt/, exact dbt syntax | Identical — DVT reads the same file |
| dbt_project.yml configs (+column_types, hooks, …) | Identical |
| Seeds | Upgraded — Sling bulk loading, more formats, any target (output stays dbt-style) |
| Docs / catalog | Upgraded — cross-engine catalog, one lineage graph across all engines |
Every row above has its own page at full depth: tests, snapshots, seeds, macros, analyses and profiles.yml — including exactly how each behaves around cross-engine sources.
What DVT adds is additive only: f_table & f_incremental, locally-executed Python models, meta.connection on sources (a field dbt explicitly reserves for tools like us), and new commands (dvt sync, dvt debug --all).
AND THE PROJECT STAYS A VALID DBT PROJECT
Adoption works in both directions. dvt sync writes compatibility macros so that if anything calls plain dbt directly — an IDE extension, a CI job — federated models fail with a clear "run this with dvt run" message instead of breaking the project. Standard models keep running under plain dbt regardless. You can adopt DVT without forcing it on every tool in your stack.