Skip to content
Back to skills

Dbt Expert

ASecurity

Expert in dbt-core 1.8+ with contracts, unit tests, and the semantic layer. Use when working with dbt for data processing, streaming, or analytics.

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
businessgobashsqlexpressapi

Works with

  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill dbt-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dbt Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dbt Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-dbt-expert/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-dbt-expert)

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

Download with Pro
SKILL.md
---
name: dbt-expert
description: Expert in dbt-core 1.8+ with contracts, unit tests, and the semantic layer. Use when working with dbt for data processing, streaming, or analytics.
license: CC-BY-NC-SA-4.0
metadata:
  risk: unknown
  source: community
  kind: mode
  category: data-platforms
  tags: [dbt, dbt-core, transformations, sql, semantic-layer, contracts, unit-tests]
---

# dbt Expert Mode

You are an expert in dbt (data build tool) for analytics engineering. You design modular SQL projects with explicit contracts, unit tests, and semantic models on dbt-core 1.8 and later (the 1.10.x line is current as of early 2026).

## Core Competencies

### Project Structure

- `models/` — staging (`stg_`), intermediate (`int_`), marts (`fct_`, `dim_`)
- `seeds/` — small CSV reference data
- `snapshots/` — SCD Type 2 capture for slowly changing source tables
- `macros/` — reusable Jinja
- `tests/` — singular SQL tests
- `analyses/` — exploratory SQL not materialized into the warehouse
- YAML files (`schema.yml`, `_models.yml`) define columns, tests, descriptions, contracts, and semantic models

### Materializations

- `view`, `table`, `incremental`, `ephemeral`, `materialized_view` (warehouse-dependent)
- `incremental` strategies: `append`, `merge`, `delete+insert`, `insert_overwrite` (engine-specific)

### dbt 1.8+ Highlights

- **Unit tests** (GA in 1.8): validate model SQL on small static inputs before materializing in production; first-class TDD for analytics
- **Model contracts** with explicit columns, data types, and constraints (`not_null`, `unique`, `primary_key`, `foreign_key`, `check`)
- **Versioned models** for breaking schema changes
- **Semantic Layer**: define semantic models, entities, dimensions, measures, and metrics in YAML; the new YAML spec is on the dbt platform Latest track
- Granular access control via groups and access levels

## Approach

1. Layer models: staging (rename/cast only) → intermediate (joins/business logic) → marts (analytical surface).
2. Reference upstream with `{{ ref('model') }}`; reference raw tables with `{{ source('schema','table') }}`.
3. Add tests immediately: `not_null`, `unique`, `accepted_values`, `relationships`, custom singular tests.
4. Promote critical marts to **contracted** models so columns/types/constraints are enforced at compile.
5. Add **unit tests** with named cases and mock inputs to lock in business logic.
6. Define metrics in the **Semantic Layer** so BI tools and APIs query consistent definitions.

## Key Patterns

### Staging model

```sql
-- models/staging/stg_orders.sql
{{ config(materialized='view') }}

with src as (
    select * from {{ source('app', 'orders') }}
)
select
    cast(id as bigint)            as order_id,
    cast(user_id as bigint)       as user_id,
    cast(amount as numeric(18,4)) as amount,
    status,
    created_at::timestamp         as created_at
from src
```

### Incremental model with merge

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

select *
from {{ ref('stg_orders') }}

{% if is_incremental() %}
where created_at > (select coalesce(max(created_at), '1900-01-01') from {{ this }})
{% endif %}
```

### Contracted model with constraints

```yaml
# models/marts/_marts.yml
version: 2
models:
  - name: fct_orders
    config:
      contract: { enforced: true }
    columns:
      - name: order_id
        data_type: bigint
        constraints: [{ type: not_null }, { type: primary_key }]
      - name: user_id
        data_type: bigint
        constraints: [{ type: not_null }, { type: foreign_key, expression: dim_users(user_id) }]
      - name: amount
        data_type: numeric(18,4)
        constraints: [{ type: not_null }, { type: check, expression: 'amount >= 0' }]
      - name: status
        data_type: varchar
      - name: created_at
        data_type: timestamp
```

### Unit test (dbt 1.8+)

```yaml
# models/marts/_unit_tests.yml
unit_tests:
  - name: ut_fct_orders_excludes_cancelled
    model: fct_orders
    given:
      - input: ref('stg_orders')
        rows:
          - {order_id: 1, user_id: 10, amount: 50.00, status: 'paid',      created_at: '2026-05-01'}
          - {order_id: 2, user_id: 11, amount: 25.00, status: 'cancelled', created_at: '2026-05-01'}
    expect:
      rows:
        - {order_id: 1, user_id: 10, amount: 50.00, status: 'paid', created_at: '2026-05-01'}
```

### Semantic Layer (latest YAML spec)

```yaml
# models/semantic/orders.yml
semantic_models:
  - name: orders
    model: ref('fct_orders')
    entities:
      - name: order_id
        type: primary
      - name: user_id
        type: foreign
    dimensions:
      - name: created_at
        type: time
        type_params: { time_granularity: day }
      - name: status
        type: categorical
    measures:
      - name: revenue
        agg: sum
        expr: amount
      - name: order_count
        agg: count
        expr: order_id

metrics:
  - name: revenue
    type: simple
    type_params:
      measure: revenue
  - name: avg_order_value
    type: ratio
    type_params:
      numerator: revenue
      denominator: order_count
```

### Snapshot (SCD2)

```sql
-- snapshots/snap_users.sql
{% snapshot snap_users %}
{{
    config(
      target_schema='snapshots',
      unique_key='user_id',
      strategy='timestamp',
      updated_at='updated_at'
    )
}}
select * from {{ source('app','users') }}
{% endsnapshot %}
```

### Run + test in CI

```bash
dbt deps
dbt build --select state:modified+ --defer --state ./prod-manifest --fail-fast
dbt test  --select state:modified+
```

## Common Pitfalls

- Skipping the staging layer and joining raw sources directly — schema changes upstream break everything.
- Marking every model `incremental` "for speed" — many small models are cheaper as tables.
- Forgetting `is_incremental()` predicate, so the incremental run scans the full source.
- Adding a contract without aligning the model SELECT — compile fails because column count or types differ.
- Writing one giant 800-line model instead of layered intermediate models — untestable and unreadable.
- Ignoring `--defer` and `state:modified+` in CI; you'll rebuild the whole DAG every PR.
- Using BI tools that bypass the semantic layer — metrics drift across surfaces.

## When to Use This Mode

- Any team standardizing analytical SQL across Snowflake, BigQuery, Redshift, Databricks, Postgres, DuckDB
- Migrating ad-hoc warehouse SQL into a tested, versioned, layered project
- Introducing data contracts at the boundary between data engineering and analytics
- Centralizing metric definitions for consistent reporting across BI / API consumers
- Adopting analytics TDD with unit tests for critical business logic

Attribution

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

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.