Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Incremental Model Patterns

ASecurity

Build reliable incremental dbt models: choose the right unique_key and strategy (append, merge, delete+insert), handle late-arriving data and out-of-order events, write a safe is_incremental filter, and design the full-refresh fallback — so the model is idempotent from day one.

7 stars
0 votes
0 copies
0 views
Added 9/23/2026
ai-agentssqltesting

Security Analysis

A100/100

Scanned 9/23/2026

$npx -y skills add mcorbett51090/RavenClaude --skill incremental-model-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Incremental Model Patterns?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Incremental Model Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mcorbett51090-incremental-model-patterns/badge)](https://www.skillsdirectory.com/skills/mcorbett51090-incremental-model-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: incremental-model-patterns
description: "Build reliable incremental dbt models: choose the right unique_key and strategy (append, merge, delete+insert), handle late-arriving data and out-of-order events, write a safe is_incremental filter, and design the full-refresh fallback — so the model is idempotent from day one."
---

# Skill: incremental-model-patterns

**Purpose:** Produce incremental dbt models that are correct under rerun, late-data, and full-refresh scenarios. Used by `analytics-engineer` (primary) and `data-quality-testing-engineer` (testing incremental correctness).

## When to use

- A fact table is large enough that a full rebuild is expensive or violates SLA (general rule: > 10M rows or > 30 minutes for a full table scan).
- A source grows mostly via append — new events, new orders, new log lines.
- The model must remain idempotent: running it twice must produce the same result as running it once.

---

## Step 1: Choose the incremental strategy

| Strategy | When to use | Key constraint |
|---|---|---|
| `append_only` (no deduplication) | Source is truly append-only and no row is ever updated | Reruns will create duplicate rows — only safe if you NEVER rerun on overlapping data |
| `merge` (default for most warehouses) | Source rows can be updated (e.g. order status changes), or you need upsert behaviour | Requires a `unique_key`; most expensive at scale |
| `delete+insert` (BigQuery, Snowflake) | Bulk-replace a time-partition or date-range in one operation | Requires a `partition_by` clause; excellent for date-partitioned fact tables with late updates |
| `insert_overwrite` (Spark/Databricks) | Partition-level replacement on Spark | Partition column must be in the `partition_by` block |

**Recommendation for most fact tables:** `merge` with a `unique_key` on the natural event grain. Upgrade to `delete+insert` only when merge cost becomes prohibitive.

---

## Step 2: Define the unique key correctly

The `unique_key` is the identifier dbt uses to decide whether to UPDATE (existing row) or INSERT (new row).

**Rules:**
1. The unique key must identify a row's grain — the combination of attributes that make one row distinct from all others.
2. For event tables, this is usually `event_id` or a composite of (`session_id`, `event_sequence_number`).
3. For slowly changing dimensions in an incremental model, it may be (`entity_id`, `valid_from`).

**Common mistakes:**

| Mistake | Symptom | Fix |
|---|---|---|
| `unique_key` is too narrow (e.g. just `date`) | Entire day's data is merged into one row | Add the natural event grain to the key |
| `unique_key` is too wide (includes a mutable field) | Rows are never matched; duplicates accumulate | Remove mutable fields from the key |
| No `unique_key` with `merge` strategy | dbt falls back to append; duplicates on rerun | Always specify `unique_key` with `merge` |

---

## Step 3: Write the is_incremental filter

The `is_incremental()` macro filters source data to only the new or updated rows when running incrementally. It is the most failure-prone part of the model.

**Standard pattern (timestamp-based, with a lookback window):**

```sql
-- models/marts/fct_orders.sql
{{
  config(
    materialized='incremental',
    unique_key='order_id',
    on_schema_change='append_new_columns'
  )
}}

select
    order_id,
    customer_id,
    order_status,
    order_amount,
    created_at,
    updated_at

from {{ ref('stg_orders') }}

{% if is_incremental() %}
  -- Lookback window handles late-arriving records and CDC lag
  -- Adjust the interval to 2x the expected source lag
  where updated_at >= (
    select dateadd(hour, -6, max(updated_at)) from {{ this }}
  )
{% endif %}
```

**Why the lookback window:**
Without it, a source record updated 2 hours after the last dbt run is missed because `updated_at > max(updated_at)` is exactly false at the boundary. A 6–24 hour lookback is a safe default; tune it to `2 × max observed source lag`.

**For event tables without an `updated_at` field (true append-only sources):**

```sql
{% if is_incremental() %}
  where event_timestamp > (select max(event_timestamp) from {{ this }})
{% endif %}
```

This is safe only when the source guarantees no late arrivals and no retroactive updates.

---

## Step 4: Handle late-arriving data

Late-arriving data — records that arrive in the source after the model has already processed their time window — are the most common cause of incremental model data quality failures.

**Detection:** run a row-count comparison between the incremental model and a reference full-scan query over the same time window. Any shortfall indicates missed late arrivals.

**Strategies:**

| Approach | How | Trade-off |
|---|---|---|
| Lookback window (recommended) | Extend `is_incremental()` filter back N hours/days | Slightly more data processed per run; simplest to reason about |
| Source watermark table | Write the last successful run timestamp to a watermark table; read it in the filter | More precise; adds operational complexity |
| Partition reprocessing | `delete+insert` strategy reprocesses the last N date partitions each run | Correct for date-partitioned sources; expensive if partitions are large |

---

## Step 5: Full-refresh fallback

Every incremental model must be safe to full-refresh. This means:

1. The full-refresh produces the same result as N sequential incremental runs over all history.
2. There is no state outside the model's own table that would cause a full-refresh to differ from the incremental result.
3. `dbt build --full-refresh` is tested in the CI pipeline at least on a subset of the model's data.

**On schema change:**
Add `on_schema_change='append_new_columns'` to the config so that new columns added to the model don't require an out-of-band table migration. For breaking schema changes (column rename, type change), a full-refresh is required; plan accordingly.

---

## Pitfalls

- **Missing lookback window** — the most common cause of silent data loss in incremental models on sources with CDC or batch lag.
- **`unique_key` on a mutable column** — the key changes, so rows are never matched; duplicates accumulate silently until a full-refresh.
- **`is_incremental()` filter references a column not in the target table** — the first run (full build) passes, but incremental runs fail because the subquery hits the not-yet-existing column. Always reference columns that exist in both the source and the target model.
- **No full-refresh test in CI** — the model appears to work incrementally but is broken for full-refresh; this surfaces as a disaster during a first production deployment or a schema migration.
- **Incremental model used for a dimension** — dimension tables with history management (SCD2) need a different pattern (dbt snapshots), not a raw incremental model; conflating them produces incorrect history.

Attribution

mcorbett51090mcorbett51090
View sourceSee grades on GitHubMore from mcorbett51090 →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Terse caveman voice: answer first, fluff gone, every technical fact kept. Use for /caveman, "caveman mode", "talk like caveman", "be brief", "less tokens". Stays on until "stop caveman" or "normal mode".

1100021 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

698461 votes

Writing Skills

Create and manage Claude Code skills in HASH repository following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns), UserPromptSubmit hook, and the 500-line rule. Includes validation and debugging with SKILL_DEBUG. Examples include rust-error-stack, cargo-dep...

3931 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3421 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Amp, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Grok Build, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

741 votes
View all in ai-agents →