Review a dbt model PR before merge
Take a pull request that adds or changes a dbt model and walk the reviewer's pass: read the diff in context, state the model's grain and check it against the unique key test, choose table vs incremental vs view deliberately, and handle late-arriving data in the incremental filter. You finish with findings a reviewer can verify before the model merges.
At a glance
| When | A PR changes a dbt model's materialisation or incremental filter; A model's unique test does not sit on its declared grain key |
|---|---|
| Where | dbt |
| Why | Grain, materialisation, and late data decide whether a model lies, and this pass checks all three before merge. |
Learning objectives
- State a model's grain in one sentence and match it to the unique test
- Justify table vs incremental vs view for a given source
- Add a lookback window so late-arriving rows are not dropped
What you need
- A pull request that adds or changes a dbt model
- The model's schema.yml with its declared grain
- [object Object]
Steps
- Read the diff and the file context. (from agent-sql-reviewer )
- Establish the grain of every result set. (from agent-sql-reviewer )
- State a model's grain and match it to its unique key test. (from skill-dbt-model-review )
- Choose table vs incremental vs view deliberately. (from skill-dbt-model-review )
- Handle late data in an incremental model. (from skill-dbt-model-review )
Check yourself
- The review states the model's grain in one sentence and the unique test sits on that key.
- An incremental model carries a lookback window such as >= max(updated_at) - interval '3 days'.
Worked example
Grain first - the unique test on the declared grain key — from skill-dbt-model-review
# One row = one ___ ; the unique test goes on that key
models:
- name: <model>
columns:
- name: <grain key>
tests:
- unique
Materialisation picker - view vs table vs incremental — from skill-dbt-model-review
view - cheap, and the model is small or rarely queried.
table - the source is huge and consumers need speed.
incremental - the source grows monotonically and you can define the watermark.
Incremental filter with a lookback window, guarded by is_incremental() — from skill-dbt-model-review
-- late-arriving rows older than the watermark are silently dropped
{% if is_incremental() %}
where updated_at >= max(updated_at) - interval '3 days'
{% endif %}
Sourced from
Security scan
Last sweep 2026-10-24: 0 finding(s), 0 marked as examples.
Common gotchas
- Late-arriving rows older than the watermark are silently dropped — add a lookback window.
- is_incremental() must guard the filter or every full run is a full reload.
- Deletes never arrive — decide explicitly: soft-delete, or periodic full-refresh.
Production considerations
- Add a lookback window (e.g. >= max(updated_at) - interval '3 days') to every incremental filter.
- Decide soft-delete vs periodic full-refresh explicitly; deletes never arrive on their own.
Diagram
dbt model review
|
|- read diff + context --> establish grain per result set
|
|- grain statement --> unique test on that key
|
|- materialisation picker --> view / table / incremental
|
|- incremental --> lookback window --> guard is_incremental()
Registry stats
| Verification | community · Oct 21, 2026 |
|---|---|
| Completeness | 81 |
| Trust multiplier | ×1.25 |
| Composite | 34.15 · adequate |
| Freshness | 3 days since last source check |
| Difficulty | intermediate |
| Version | 0.1.0 |
| License | CC-BY-4.0 |
| Security flags | none |
Did this solve your problem?
Review a dbt model PR before merge