Preview what DDL changes dbt run will execute — like terraform plan for dbt
Project description
dbt-plan
Preview what DDL changes dbt run will execute — before you run it.
Like terraform plan for dbt. Works with major warehouses supported by dbt (Snowflake, BigQuery, Redshift, Postgres, etc.).
Goal
Catch destructive DDL changes (like DROP COLUMN) before they reach production. dbt's incremental + sync_all_columns can silently execute ALTER TABLE when SELECT columns change. dbt-plan detects this at PR time and blocks the merge.
Quick Start
pip install git+https://github.com/ab180/dbt-plan@v0.2.0
# In your dbt project directory:
dbt-plan init # Generate .dbt-plan.yml config + update .gitignore
dbt-plan stats # Analyze project readiness
dbt compile
dbt-plan snapshot # Save baseline (compiled SQL + manifest)
# ... make model changes ...
dbt compile
dbt-plan check # See what will change
dbt-plan check --format github # GitHub markdown output
dbt-plan check --format json # JSON for CI pipelines
dbt-plan check --select model1 # Check specific model only
Output Example
$ dbt-plan check
dbt-plan -- 2 model(s) changed
DESTRUCTIVE int_unified (incremental, sync_all_columns)
DROP COLUMN data__device
DROP COLUMN data__user
ADD COLUMN data__device__uuid
Downstream: dim_device, fct_events (2 model(s))
SAFE dim_device (table)
CREATE OR REPLACE TABLE
What Works (v0.2.0)
| Feature | Status | Details |
|---|---|---|
| Column extraction (SQLGlot) | Done | Multi-dialect (Snowflake, BigQuery, Postgres, etc.) |
| DDL prediction | Done | All materialization x on_schema_change combinations |
| Downstream impact | Done | Memoized batch BFS, cycle protection |
| Removed model detection | Done | Always DESTRUCTIVE (ephemeral = SAFE) |
| Parse failure safety | Done | Never returns SAFE when columns unknown |
| SELECT * fallback | Done | Manifest column definitions as fallback |
| Output formats | Done | --format text (color) / github / json |
| Configuration | Done | .dbt-plan.yml + env vars (DBT_PLAN_*) |
| Commands | Done | snapshot, check, init, stats |
| Model filtering | Done | --select model1,model2 / ignore_models in config |
| Package filtering | Done | Auto-excludes dbt package models |
| CI integration | Done | Lint (ruff) + test + 90% coverage, CI workflow template |
| Verbose mode | Done | --verbose / -v for debugging |
What Doesn't Work Yet
| Feature | Phase | Why It Matters |
|---|---|---|
| INFORMATION_SCHEMA query | 2a | For SELECT * models without manifest column definitions |
ddl-reviewed label override |
2b | Escape hatch for intentional destructive changes |
| Slack notifications | 2c | Alert on destructive DDL |
dbt-plan run wrapper |
2b | Interactive confirmation before dbt run |
| Column type detection | 2c | ALTER TYPE predictions (only 5.2% of columns have type info) |
DDL Prediction Rules
| Materialization | on_schema_change | Predicted DDL | Safety |
|---|---|---|---|
| table | any | CREATE OR REPLACE TABLE |
SAFE |
| view | any | CREATE OR REPLACE VIEW |
SAFE |
| incremental | ignore | no DDL | SAFE |
| incremental | fail | build failure | WARNING |
| incremental | append_new_columns | ADD COLUMN only |
SAFE |
| incremental | sync_all_columns | ADD + DROP COLUMN |
DESTRUCTIVE if columns removed |
| any | (model removed) | MODEL REMOVED |
DESTRUCTIVE |
CI Integration (GitHub Actions)
name: dbt-plan
on:
pull_request:
paths: ['models/**', 'macros/**']
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- run: pip install uv && uv sync
- run: pip install git+https://github.com/ab180/dbt-plan@v0.1.0
# Compile and snapshot base branch
- run: |
git checkout ${{ github.event.pull_request.base.sha }}
dbt compile
dbt-plan snapshot
# Compile current and check
- run: |
git checkout ${{ github.event.pull_request.head.sha }}
dbt compile
dbt-plan check --format github >> $GITHUB_STEP_SUMMARY
# Block destructive changes (exit 1)
- run: dbt-plan check
How It Works
flowchart TD
A[dbt-plan snapshot] --> B[Save compiled SQL + manifest.json]
C[dbt-plan check] --> D[diff_compiled_dirs]
D --> E[base compiled SQL]
D --> F[current compiled SQL]
E --> G[extract_columns]
F --> H[extract_columns]
G --> I[base columns]
H --> J[current columns]
I --> K[column diff]
J --> K
K --> L[predict_ddl + manifest config]
L --> M{Safety?}
M -->|SAFE| N[exit 0]
M -->|WARNING| O[exit 2]
M -->|DESTRUCTIVE| P[exit 1 — block merge]
L --> Q[find_downstream]
Q --> R[format_text / format_github]
Contributing
See CONTRIBUTING.md for development setup, TDD workflow, and coding rules.
Architecture
src/dbt_plan/
├── columns.py # SQLGlot column extraction
├── predictor.py # DDL prediction rules
├── manifest.py # manifest.json parsing + downstream BFS
├── diff.py # compiled SQL directory comparison
├── formatter.py # text / GitHub markdown output
└── cli.py # CLI entry point (snapshot, check)
How to Contribute
Good first issues:
- Add more compiled SQL fixtures in
tests/fixtures/for edge cases - Improve error messages for common mistakes
- Add
--verboseflag for debugging
Medium issues:
- INFORMATION_SCHEMA integration (Phase 1b) — query warehouse for actual columns
- CI workflow template — reusable GitHub Actions for dbt projects
- PR comment posting with
<!-- dbt-plan -->marker
Design decisions: See docs/architecture-decisions.md and docs/design-spec.md.
Supported
- dbt-core 1.7+ with dbt-snowflake
- Python 3.10+
- Snowflake VARIANT (
col:path::TYPE), CTE, QUALIFY, window functions
License
Apache-2.0
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dbt_plan-0.2.0.tar.gz.
File metadata
- Download URL: dbt_plan-0.2.0.tar.gz
- Upload date:
- Size: 285.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
33d1cef0c0e30fa8a9045175aceba2ef9c6959975b4b88f2dc50736b96966b21
|
|
| MD5 |
330df58805c43c05b4d89e9db9474e06
|
|
| BLAKE2b-256 |
05cfabfea9d9db166c3269be7dc7fdbc09f1234197ba06b59d943d0298df9a19
|
File details
Details for the file dbt_plan-0.2.0-py3-none-any.whl.
File metadata
- Download URL: dbt_plan-0.2.0-py3-none-any.whl
- Upload date:
- Size: 23.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31a68fef9f9218c7623aa615922c83c338bcdadfc844bc37a39660a702e4609b
|
|
| MD5 |
6a83b7e165296185e241453fcaff5bea
|
|
| BLAKE2b-256 |
a0745530828f76ef36cbc053f61bfe5ce534d3f65d5e3c53703af133e54bdf97
|