Dry-run your dbt models before you pay for them
BigQuery will tell you what a query costs before it runs. Here is how to put that number in the pull request, using a small open-source tool I wrote
On BigQuery's on-demand pricing you pay per byte scanned. A dbt model that reads a whole table where it used to read a filtered slice costs more every time it runs, and nothing in the usual review loop shows that. The SQL compiles. The tests pass. The CI is green. The bill arrives a month later.
BigQuery already knows the answer. Every query can be submitted as a dry run, which returns the bytes it would process without executing it, at no charge. The gap is that dry runs live in the console or the API, not in the pull request where the decision is made.
Putting the number in the review
dbt-cost is a small Python CLI that closes that gap. It reads the compiled dbt manifest, resolves the models you ask for using dbt's own selector syntax, sends each model's SQL to the dry-run endpoint, and prices the result.
pip install dbt-cost
dbt compile
dbt-cost estimate "stg_orders+"That gives a table of the selected models with bytes scanned and the estimated cost at the on-demand rate (default $6.25 per TB, changeable with --price). dbt-cost report does the whole project, grouped by layer, so you can see which marts are carrying the bill.
The useful command is diff. Compile the manifest on the base branch and on the pull request, then:
dbt-cost diff --base-manifest base/manifest.json --pr-manifest pr/manifest.json --format markdown --threshold 10It prints the models whose cost changed, with before, after and the difference, as Markdown that a GitHub Action can post as a sticky comment on the pull request. With --threshold it exits non-zero when the increase crosses the limit, so the check fails and the reviewer sees why. The repository has a workflow that does the two compiles and the comment end to end.
What it cannot do
Dry-run mode is hard-coded. The tool cannot execute a query or read data; it needs only the BigQuery Job User role and read access to the tables the models reference, and it authenticates with Application Default Credentials by default. It is BigQuery only. And a dry run estimates bytes scanned, which is not the whole bill: slot-based pricing, storage and incremental models that scan less at run time than the compiled SQL suggests all need judgement on top.
Why bother
Because cost is a code review question. The engineer who removed a partition filter can put it back in a minute if the number is in front of them. Finding it on the invoice is a different conversation with different people in it.
The pattern generalises beyond dbt and beyond BigQuery: if a system can tell you what an action will cost before you take it, wire that into the place the action is reviewed.