DiffCost documentation
DiffCost shows the dollar cost of BigQuery SQL and dbt changes in your pull requests, priced per query run, and can block merges that make queries too expensive.
What DiffCost does#
DiffCost is a GitHub Action with an optional dashboard. On every pull request (PR) it:
- finds the SQL files or dbt models the PR changes;
- dry-runs the base version and the PR version of each one in your own BigQuery project (a dry run is free and returns bytes scanned, never rows);
- prices the difference per query run, and parses both versions to flag risky changes such as a removed partition filter;
- posts one comment on the PR with the result.
An excerpt of a comment:
π° DiffCost β BigQuery Cost Report (cost per run)
Query / Model Current This PR Scan Delta Cost Change fct_daily_revenue$2.09 $9.66 +1.21 TiB +$7.57 (β362%) stg_orders$0.031 $0.031 0 B β TOTAL $2.12 $9.69 +1.21 TiB +$7.57 (β357%) β οΈ This PR increases query costs by $7.57/run (+357%, β$227.10/mo at 30 runs/mo).
π Root cause analysis: π΄
fct_daily_revenue: Partition filter removed:order_date >= '2024-01-01'
The full comment also has a line for the largest single-query increase, the change in bytes scanned and, here, a High Scan Warning. Reading the PR comment shows a full report and every line it can contain.
With an API key, on any plan, the Action also sends its results to the dashboard (30 days of history on Free) and uses the dashboard's pricing settings. Once the DiffCost GitHub App is installed, every PR also gets a diffcost/cost-check commit status; on Free it always passes. Without the App, no status is posted, and the PR comment says the App isn't installed. On a paid plan (Starter, Team or Enterprise) or during the 14-day Team trial, DiffCost also:
- evaluates your cost thresholds and posts a failing
diffcost/cost-checkstatus when a PR exceeds them, which blocks the merge when you make it a required check; - sends Slack alerts when a PR exceeds your cost policy;
- charts cost trends and, on Team, cost per PR author.
Your SQL goes only to BigQuery, in your project. With an API key, the DiffCost service receives the repository name, PR number, commit SHA, the PR author (only with send_author: true), the .diffcost.yml thresholds and runs per month, whether the run came from a merge queue, and numbers: per-run costs and bytes (PR totals, and per query with no identifier) and counts of analyzed and unknown queries. It never receives SQL, table or model names, or file paths. See Security and data.
Requirements and limits#
- GitHub Actions on a Linux runner with Docker. DiffCost is a Docker action; GitHub-hosted
ubuntu-latestrunners work. - BigQuery and a Google Cloud project where a service account can run dry-run jobs and read the tables your SQL uses.
- Plain SQL files must run as written: each file must run as written in GoogleSQL, with qualified table names and no templating or query parameters. See What DiffCost can price.
- dbt Core with
dbt compilein the workflow; dbt Cloud isn't required. - One DiffCost run per PR. It covers one dbt project, or one set of plain SQL files. A second run on the same PR overwrites the first run's comment and status.
- Paid features need a GitHub organization, not a personal account. Only its GitHub organization owners can create API keys, set dashboard thresholds and manage billing.
- Merge queues need the workflow to run on
merge_grouptoo. DiffCost then postsdiffcost/cost-checkon the queue's commit, so the required check works with a queue. See Merge queues.
Features by plan#
| Free | Starter ($49/mo, 5 seats) | Team ($149/mo, 20 seats) | |
|---|---|---|---|
| PR comment with per-run cost (BigQuery dry run) | β | β | β |
Structural risk flags (removed partition filters, SELECT *, new JOINs) |
β | β | β |
Merge blocking: org-wide threshold in the dashboard, or each repository's .diffcost.yml |
β | β | β |
| Slack alerts | β | β | β |
| Dashboard comparison history (needs an API key) | 30 days | 12 months | Unlimited |
| Cost trends chart | β | β | β |
| Per-repository thresholds in the dashboard | β | β | β |
Cost attribution per PR author (needs send_author: true) |
β | β | β |
The Free plan needs no account and no API key. For more than 20 seats or an audit log, ask about Enterprise at support@diffcost.dev. Details: Plans and billing.
Set up DiffCost#
- Quickstart: add the Action to a repository and get your first cost report. This needs no DiffCost account.
- BigQuery access and keyless auth: switch to keyless access (recommended).
- dbt projects: if you use dbt, compile manifests and keep a baseline.
- Merge blocking: block merges that exceed your cost policy.
Do steps 1 to 3 on the Free plan first. Have a GitHub organization owner sign in to the dashboard when you're ready for step 4: the first owner sign-in starts the 14-day Team trial.
Find what you need#
| I want to⦠| Read |
|---|---|
| understand what DiffCost measures and how | How DiffCost works |
| read a cost report, or a Cost unavailable row | Reading the PR comment |
| choose and change cost limits, and get Slack alerts | Thresholds and alerts |
| use the dashboard: history, trends, settings, team | Using the dashboard |
| track costs, and check that DiffCost keeps enforcing | Monitoring |
| update the Action, rotate keys, add repositories, offboard | Maintenance and updates |
| fix an error in the comment or the log | Troubleshooting |
look up an input, an output or a .diffcost.yml key |
Configuration reference |
Terms used in these docs#
- Per run: every dollar figure is the cost of running a query once. The monthly figure ("β$X/mo") is a projection: per-run cost Γ runs per month (30 unless you change it).
- Worst single query: thresholds compare against the largest increase of any one query in the PR, not the PR's total.
- Base commit: the commit the PR is compared against. DiffCost uses
base_refif you set it; otherwise, with the default checkout of GitHub's merge commit, the base-branch commit that merge commit was built on (the base branch's tip at the PR's latest push); otherwise the PR's base commit (pull_request.base.sha). - Base cost: the per-run cost of the base version of a query, shown in the comment's Current column.
- Unknown cost: a query DiffCost couldn't price, for example because the dry run failed. The comment shows it as a Cost unavailable row or a "dry-run failed" row. DiffCost never counts it as $0, and it blocks the PR when a threshold applies.
- Not costed (dbt only): a model whose PR dry run fails only because it reads objects the same PR creates. It counts as an unknown cost only when structural analysis also flags a risk on it, or can't run on a model that already exists.
- Baseline manifest: for dbt, the
manifest.jsoncompiled from your main branch, which DiffCost compares the PR's manifest against. - Cost policy: the thresholds that apply to a repository. Each row under the dashboard's Merge-blocking thresholds is one threshold.
- Verdict: DiffCost's conclusion about a PR: blocked, over the policy but not enforced, enforcement unconfirmed, or within the policy.
- DiffCost service: the hosted part of DiffCost. With an API key, the Action sends it the numbers; it applies your thresholds and posts the
diffcost/cost-checkstatus. - DiffCost admin: a GitHub organization owner. Admins create API keys, set dashboard thresholds and manage billing. Other members can view.
- Seat: a person who uses the dashboard. CI runs never need a seat.
Something missing or wrong? Email support@diffcost.dev.