finowl diff
finowl diff [OPTIONS] -b, --bundle PATH Path to the databricks.yml bundle file. [default: databricks.yml] -t, --target TEXT Target environment to act on. [required] -o, --output [table|json] Output format. [default: table] -p, --profile TEXT Authentication profile to act as. [env: FINOWL_CONFIG_PROFILE]
Examples
# what would this branch change about dev?
finowl diff --target dev
# the delta a release to production would make
finowl diff -t prod
# for a script
finowl diff -t prod -o json | jq '.result.total_delta'What it needs
Unlike breakdown, this command reads the deployed state, so it needs to reach your workspace.
- The Databricks CLI on
PATH. Finowl runsdatabricks bundle plan --target <t>and reads the plan it produces. - Workspace credentials configured the way you already configure them for
databricks bundle deploy. Finowl never handles them itself and never provisions, modifies or deletes anything. - A workload word for every compute in the bundle for this target, the same gate
breakdownapplies.
How the comparison is made
Both sides of the diff come from the same bundle plan — its deployed state and its planned state — and both are priced with the same workload words from finowl.yml.
Continuous pipelines are a special case: the Databricks CLI does not emit continuous: true into the plan it generates, so Finowl reads that flag back from the bundle YAML, which is authoritative for it. Without that, a continuous pipeline would be priced as if it ran on a schedule.
Reading the output
$ finowl diff --target prod
╭───────────────────────────────────────────────────╮
│ Cost Diff — Deployed vs Current │
│ Pricing Model: Azure Pay As You Go — Premium tier │
╰───────────────────────────────────────────────────╯
┏━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Job / Cluster Group ┃ Instance ┃ Workers ┃ Monthly Hours ┃ Before (€) ┃ After (€) ┃ Δ Monthly ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━┩
│ ~ daily_ingest [light] │ │ │ │ €44.64 │ €89.28 │ +€44.64 │
│ main │ Standard_DS3_v2 │ 4 → 8 │ 37.2h │ 44.64 │ 89.28 │ +44.64 │
│ = customer_scd [medium]│ │ │ │ €102.20 │ €102.20 │ — │
│ default │ Standard_DS4_v2 │ 2 │ 73.0h │ 102.20 │ 102.20 │ — │
│ - legacy_export [light]│ │ │ │ €22.30 │ €0.00 │ -€22.30 │
│ main │ Standard_DS3_v2 → — │ 2 → — │ 0.0h │ 22.30 │ 0.00 │ -22.30 │
└────────────────────────┴──────────────────────┴─────────┴───────────────┴────────────┴───────────┴───────────┘
Total monthly delta: +€22.34| COLUMN | MEANING |
|---|---|
| Job / Cluster Group | The resource and the compute inside it. One row per compute, not per resource — a job with two clusters produces two rows. |
| Instance / Workers | Before and after, shown as 4 → 8 when they change. A dash on one side means the compute is being created or removed. |
| Monthly Hours | Derived from the schedule and the workload word. Unchanged by a resize, which is why a resize shows up entirely in the cost columns. |
| Δ Monthly | After minus before. Positive adds cost, negative removes it, a dash means no movement. |
When nothing changed
A branch that touches no compute produces an empty diff and a zero delta. That is a result, not an error — diff exits 0.
diff and guard
guard computes the same diff internally and then evaluates it against your rules. So why run both?
| COMMAND | ANSWERS | EXIT CODE |
|---|---|---|
| finowl diff | What moved, resource by resource. Readable in a pull request without knowing any rules. | 0 unless it could not run |
| finowl guard | Whether what moved is allowed here. | 1 on a blocking violation |
In CI you generally want guard, and diff when you want the detail visible without a verdict attached. Locally, diff is the fastest way to see whether a change you are about to push is expensive.
JSON output
{
"command": "diff",
"target": "prod",
"passed": true,
"result": {
"total_delta": 22.34,
"diffs": [
{
"resource_name": "daily_ingest",
"resource_type": "databricks_job",
"action": "update",
"before_total": 44.64,
"after_total": 89.28,
"delta": 44.64,
"workload_before": "light",
"workload_after": "light",
"before_groups": [ ... ],
"after_groups": [ ... ]
}
]
}
}workload_before and workload_after carry the words each side was priced with. When they differ, the delta includes an assumption change and the number should be read accordingly.
Next: finowl guard, which turns this diff into a verdict.