refactoring-dbt-models — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited refactoring-dbt-models (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Find ALL downstream dependencies before changing. Refactor in small steps. Verify output after each change.
cat models/<path>/<model_name>.sqlIdentify refactoring opportunities:
CRITICAL: Never refactor without knowing impact.
# Get full dependency tree (model and all its children)
dbt ls --select model_name+ --output list
# Find all models referencing this one
grep -r "ref('model_name')" models/ --include="*.sql"Report to user: "Found X downstream models: [list]. These will be affected by changes."
BEFORE changing any columns, check what downstream models reference:
# For each downstream model, check what columns it uses
cat models/<path>/<downstream_model>.sql | grep -E "model_name\.\w+|alias\.\w+"If downstream models reference specific columns, you MUST ensure those columns remain available after refactoring.
| Opportunity | Strategy |
|---|---|
| Long CTE | Extract to intermediate model |
| Repeated logic | Create macro in macros/ |
| Complex join | Split into intermediate models |
| Multiple concerns | Separate into focused models |
#### Pattern: Extract CTE to Model
Before:
-- orders.sql (200 lines)
with customer_metrics as (
-- 50 lines of complex logic
),
order_enriched as (
select ...
from orders
join customer_metrics on ...
)
select * from order_enrichedAfter:
-- customer_metrics.sql (new file)
select
customer_id,
-- complex logic here
from {{ ref('customers') }}
-- orders.sql (simplified)
with order_enriched as (
select ...
from {{ ref('raw_orders') }} orders
join {{ ref('customer_metrics') }} cm on ...
)
select * from order_enriched#### Pattern: Extract to Macro
Before (repeated in multiple models):
case
when amount < 0 then 'refund'
when amount = 0 then 'zero'
else 'positive'
end as amount_categoryAfter:
-- macros/categorize_amount.sql
{% macro categorize_amount(column_name) %}
case
when {{ column_name }} < 0 then 'refund'
when {{ column_name }} = 0 then 'zero'
else 'positive'
end
{% endmacro %}
-- In models:
{{ categorize_amount('amount') }} as amount_category# Compile to check syntax
dbt compile --select +model_name+
# Build entire lineage
dbt build --select +model_name+
# Check row counts (manual)
# Before: Record expected counts
# After: Verify counts matchCRITICAL: Refactoring should not change output.
# Compare row counts before and after
dbt show --inline "select count(*) from {{ ref('model_name') }}"
# Spot check key values
dbt show --select <model_name> --limit 10If changing output columns:
| Symptom | Refactoring |
|---|---|
| Model > 200 lines | Extract CTEs to models |
| Same logic in 3+ models | Extract to macro |
| 5+ joins in one model | Create intermediate models |
| Hard to understand | Add CTEs with clear names |
| Slow performance | Split to allow parallelization |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.