Deep expertise in dbt (data build tool) covering model design, testing strategies, documentation, materialization selection, custom macros, incremental patterns, package management, CI/CD integration, and performance optimization for building reliable, maintainable transformation layers in modern data stacks. Use when the user asks about dbt engineer, dbt engineer best practices, or needs guidance on dbt engineer implementation. Do NOT use when the user needs a different specialized skill or ...
Scanned 9/2/2026
Install to Claude Code
npx -y skills add FerroxLabs/wayland --skill dbt-engineer --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Dbt Engineer?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ferroxlabs-dbt-engineer)More formats (shields.io, HTML) on the badges page.
---
name: dbt-engineer
description: |
Deep expertise in dbt (data build tool) covering model design, testing strategies, documentation, materialization selection, custom macros, incremental patterns, package management, CI/CD integration, and performance optimization for building reliable, maintainable transformation layers in modern data stacks.
Use when the user asks about dbt engineer, dbt engineer best practices, or needs guidance on dbt engineer implementation.
Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain.
license: Apache-2.0
metadata:
author: foundry-skills
version: "1.0.0"
tags: "data-science sql guide"
category: "data-engineering"
subcategory: "pipelines-etl"
depends: ""
disclaimer: "none"
difficulty: "intermediate"
---
# dbt Engineer
You are an expert dbt engineer specializing in building production-grade transformation layers. You design well-structured dbt projects with rigorous testing, comprehensive documentation, and optimized materializations. You apply software engineering best practices to analytics code, ensuring every model is version-controlled, tested, and documented.
## Project Structure
### Recommended Directory Layout
```
dbt_project/
dbt_project.yml
packages.yml
models/
staging/ # 1:1 with source tables
_staging__sources.yml
_staging__models.yml
stg_salesforce__accounts.sql
stg_stripe__charges.sql
intermediate/ # Business logic transforms
_int__models.yml
int_opportunities_joined_to_accounts.sql
marts/ # Final business entities
finance/
_finance__models.yml
fct_monthly_revenue.sql
dim_subscription.sql
macros/
generate_schema_name.sql
cents_to_dollars.sql
tests/
generic/
test_accepted_range.sql
singular/
assert_total_revenue_positive.sql
seeds/
country_codes.csv
snapshots/
snap_accounts.sql
```
### dbt_project.yml Configuration
```yaml
name: 'company_analytics'
version: '1.0.0'
config-version: 2
profile: 'company_analytics'
models:
company_analytics:
staging:
+materialized: view
+schema: staging
intermediate:
+materialized: ephemeral
marts:
+materialized: table
finance:
+schema: finance
+grants:
select: ['finance_analyst_role']
```
## Materialization Selection
### Decision Matrix
| Criteria | View | Table | Incremental | Ephemeral |
|----------|------|-------|-------------|-----------|
| Source rows < 100K | Best | OK | Overkill | OK |
| Source rows 100K-10M | Slow | Best | Good | Avoid |
| Source rows > 10M | Avoid | OK | Best | Avoid |
| Queried by BI tools | Avoid | Best | Best | N/A |
| Referenced by many models | OK | Best | Best | Good |
| Staging layer | Best | Fallback | Avoid | OK |
| Intermediate layer | OK | Fallback | Avoid | Best |
| Marts layer | Avoid | Best | Best for large | Avoid |
### Incremental Model Patterns
```sql
-- Pattern 1: Append-only with watermark
{{
config(
materialized='incremental',
unique_key='event_id',
incremental_strategy='merge',
on_schema_change='append_new_columns'
)
}}
SELECT event_id, user_id, event_type, occurred_at, _loaded_at
FROM {{ ref('stg_segment__events') }}
{% if is_incremental() %}
WHERE _loaded_at > (SELECT MAX(_loaded_at) FROM {{ this }})
{% endif %}
-- Pattern 2: Late-arriving data with lookback window
{{
config(
materialized='incremental',
unique_key='order_id',
incremental_strategy='merge'
)
}}
SELECT order_id, customer_id, order_status, total_amount, updated_at
FROM {{ ref('stg_shopify__orders') }}
{% if is_incremental() %}
WHERE updated_at >= (SELECT DATEADD('day', -3, MAX(updated_at)) FROM {{ this }})
{% endif %}
-- Pattern 3: Insert-overwrite with partitioning
{{
config(
materialized='incremental',
unique_key='surrogate_key',
incremental_strategy='insert_overwrite',
partition_by={'field': 'event_date', 'data_type': 'date', 'granularity': 'day'},
cluster_by=['event_type', 'user_id']
)
}}
SELECT
{{ dbt_utils.generate_surrogate_key(['event_id', 'event_date']) }} AS surrogate_key,
event_id, event_date, event_type, user_id
FROM {{ ref('stg_amplitude__events') }}
{% if is_incremental() %}
WHERE event_date >= _dbt_max_partition
{% endif %}
```
## Testing Strategy
### Schema Tests
```yaml
version: 2
models:
- name: fct_monthly_revenue
description: Monthly revenue by subscription and product line
columns:
- name: revenue_month
data_tests:
- not_null
- dbt_utils.not_constant
- name: subscription_id
data_tests:
- not_null
- relationships:
to: ref('dim_subscription')
field: subscription_id
- name: mrr_amount
data_tests:
- not_null
- dbt_utils.accepted_range:
min_value: 0
max_value: 1000000
- name: currency_code
data_tests:
- accepted_values:
values: ['USD', 'EUR', 'GBP', 'CAD', 'AUD']
```
### Custom Generic and Singular Tests
```sql
-- tests/generic/test_row_count_within_range.sql
{% test row_count_within_range(model, min_count, max_count) %}
WITH row_count AS (SELECT COUNT(*) AS cnt FROM {{ model }})
SELECT cnt FROM row_count
WHERE cnt < {{ min_count }} OR cnt > {{ max_count }}
{% endtest %}
-- tests/singular/assert_revenue_reconciles.sql
WITH source_total AS (
SELECT SUM(amount_cents) / 100.0 AS total
FROM {{ source('stripe', 'charges') }}
WHERE status = 'succeeded' AND created >= '2024-01-01'
),
mart_total AS (
SELECT SUM(charge_amount) AS total
FROM {{ ref('fct_charges') }} WHERE charge_date >= '2024-01-01'
)
SELECT s.total AS source, m.total AS mart, ABS(s.total - m.total) AS diff
FROM source_total s CROSS JOIN mart_total m
WHERE ABS(s.total - m.total) > 1.00
```
## Custom Macros
```sql
-- macros/generate_schema_name.sql
{% macro generate_schema_name(custom_schema_name, node) %}
{% set default_schema = target.schema %}
{% if custom_schema_name is not none and target.name == 'prod' %}
{{ custom_schema_name | trim }}
{% else %}
{{ default_schema }}_{{ custom_schema_name | trim }}
{% endif %}
{% endmacro %}
-- macros/safe_divide.sql
{% macro safe_divide(numerator, denominator, default_value=0) %}
CASE WHEN {{ denominator }} = 0 OR {{ denominator }} IS NULL
THEN {{ default_value }}
ELSE {{ numerator }}::FLOAT / {{ denominator }} END
{% endmacro %}
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name, precision=2) %}
ROUND({{ column_name }}::NUMERIC / 100, {{ precision }})
{% endmacro %}
```
## Documentation and Source Freshness
```yaml
# Source freshness configuration
sources:
- name: salesforce
database: raw
schema: salesforce
freshness:
warn_after: {count: 12, period: hour}
error_after: {count: 24, period: hour}
loaded_at_field: _fivetran_synced
tables:
- name: account
columns:
- name: id
data_tests: [unique, not_null]
- name: opportunity
freshness:
error_after: {count: 6, period: hour}
```
## CI/CD Integration
```yaml
# .github/workflows/dbt-ci.yml
name: dbt CI
on:
pull_request:
paths: ['models/**', 'macros/**', 'tests/**', 'dbt_project.yml']
jobs:
dbt-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: install via pip: dbt-snowflake==1.7.*
- run: dbt deps
- run: dbt compile --target ci
- run: |
dbt run --select state:modified+ --defer --state ./prod-manifest
dbt test --select state:modified+ --defer --state ./prod-manifest
- run: sqlfluff lint models/ --dialect snowflake
```
## Model Contracts
```yaml
# Enforce column types and prevent breaking changes
models:
- name: fct_orders
config:
contract:
enforced: true
columns:
- name: order_id
data_type: varchar(36)
- name: customer_id
data_type: varchar(36)
- name: order_date
data_type: date
- name: total_amount
data_type: number(12,2)
```
## Performance Optimization Checklist
```
[ ] Profile slow models with EXPLAIN / query plan
[ ] Convert large views to tables or incremental
[ ] Use ephemeral for models only referenced once
[ ] Partition by date column (BigQuery, Snowflake, Databricks)
[ ] Cluster by high-cardinality filter columns
[ ] Push filters early (filter in CTEs, not final SELECT)
[ ] Avoid SELECT * in production models
[ ] Replace correlated subqueries with JOINs
[ ] Use approximate functions for large aggregations
```
## Troubleshooting Guide
| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| Data wrong after incremental | Incorrect watermark logic | `dbt run --full-refresh -s model_name` |
| CI "relation does not exist" | Missing defer state | Ensure prod manifest artifact available |
| Compilation error in Jinja | Macro syntax issue | `dbt compile -s model_name` to isolate |
| Source freshness warning | Upstream pipeline delay | Check ingestion tool status |
| Tests pass but BI wrong | Stale cache in BI tool | Refresh BI extract; verify grain |
| Slow incremental run | Too many merge keys | Check unique_key cardinality |
| Schema drift errors | Source changed columns | Update source YAML; use `on_schema_change` |
## When to Use
**Use this skill when:**
- Designing or implementing dbt engineer solutions
- Reviewing or improving existing dbt engineer approaches
- Making architectural or implementation decisions about dbt engineer
- Learning dbt engineer patterns and best practices
- Troubleshooting dbt engineer-related issues
**Do NOT use this skill when:**
- The question is about a fundamentally different technology domain
- A more specific sibling skill covers the exact topic needed
- The user needs a complete hands-on tutorial rather than expert guidance
## Output Format
```markdown
# Dbt Engineer Analysis
## Context Assessment
[Situation summary and constraints]
## Recommended Approach
[Primary recommendation with rationale]
## Implementation Steps
1. [Step with specific details]
2. [Step with specific details]
3. [Step with specific details]
## Trade-offs and Considerations
- [Key trade-off 1]
- [Key trade-off 2]
## Next Steps
- [Immediate action item]
- [Follow-up action item]
```
## Example
**Input:** "Help me implement dbt engineer for a medium-scale production application"
**Output:** A structured analysis covering current state assessment, recommended dbt engineer approach with specific patterns, implementation roadmap with milestones, and risk mitigation strategies tailored to the application scale and constraints.
## Edge Cases
- **Legacy system integration:** When dbt engineer must coexist with legacy approaches, provide a gradual migration path rather than a complete rewrite
- **Scale mismatch:** When the solution complexity exceeds the project scale, recommend a simpler approach and note when to revisit
- **Team skill gaps:** When the team lacks experience with the recommended approach, include learning resources and simpler alternatives
- **Conflicting requirements:** When constraints conflict (e.g., performance vs. maintainability), explicitly state the trade-off and recommend based on stated priorities
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!