refactoring-dbt-models について
小さな変更を検証しながら dbt モデルをリファクタリングするスキルです。下流の依存関係を追跡し、出力を維持しつつ、関連する文書やテストの更新を支援します。
主な機能
- 01下流の依存関係を追跡
- 02下流モデルのカラム使用を確認
- 03CTE や重複ロジックを抽出
- 04モデルの依存範囲をコンパイル・ビルド
- 05変更後に出力を比較
活用シーン
- 01長い dbt モデルを分割
- 02重複ロジックをマクロに移動
- 03モデルのカラム変更前に影響を評価
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ altimateai/ data-engineering-skills --skill refactoring-dbt-modelsインストール後、Claude Code で/refactoring-dbt-modelsを入力して読み込まれたか確認します。
詳細
dbt のリファクタリング
変更を加える前に、下流の依存関係をすべて確認してください。小さなステップでリファクタリングしてください。変更のたびに出力を検証してください。
ワークフロー
1. 現在のモデルを分析する
cat models/<path>/<model_name>.sql
リファクタリングの機会を特定します。
- 50 行を超える CTE → 中間モデルに抽出する
- 複数のモデルでロジックが繰り返されている → マクロに抽出する
- 複数の結合が連続している → ステップに分割する
- 複雑な WHERE 句 → ステージング用のフィルターに抽出する
2. 下流の依存関係をすべて確認する
重要:影響範囲を把握せずにリファクタリングしないでください。
# 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"
ユーザーに報告します:「下流モデルが X 個見つかりました:[一覧]。これらは変更の影響を受けます。」
3. 下流モデルで使用されている列を確認する
列を変更する前に、下流モデルがどの列を参照しているか確認してください。
# For each downstream model, check what columns it uses
cat models/<path>/<downstream_model>.sql | grep -E "model_name\.\w+|alias\.\w+"
下流モデルが特定の列を参照している場合は、リファクタリング後もそれらの列を引き続き使用できるようにする必要があります。
4. リファクタリング戦略を計画する
| 機会 | 戦略 |
|---|---|
| 長い CTE | 中間モデルに抽出する |
| 繰り返されるロジック | macros/ にマクロを作成する |
| 複雑な結合 | 中間モデルに分割する |
| 複数の関心事 | 用途を絞ったモデルに分ける |
5. リファクタリングを実行する
パターン:CTE をモデルに抽出する
変更前:
-- 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_enriched
変更後:
-- 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
パターン:マクロに抽出する
変更前(複数のモデルで繰り返されている場合):
case
when amount < 0 then 'refund'
when amount = 0 then 'zero'
else 'positive'
end as amount_category
変更後:
-- 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
6. 変更を検証する
# 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 match
7. 出力が元の出力と一致することを確認する
重要:リファクタリングによって出力を変えてはいけません。
# 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 10
8. 下流モデルを更新する
出力列を変更する場合:
- 下流の参照をすべて更新する
- schema.yml のドキュメントを更新する
- 下流のテストを再実行する
リファクタリングのチェックリスト
- 下流の依存関係をすべて特定した
- 影響範囲をユーザーに伝えた
- 一度に 1 つの変更を行う
- 変更のたびにコンパイルが成功する
- 変更のたびにビルドが成功する
- 出力を検証した(行数が一致する)
- ドキュメントを更新した
- テストが引き続き成功する
よくあるリファクタリングのきっかけ
| 症状 | リファクタリング |
|---|---|
| モデルが 200 行を超える | CTE をモデルに抽出する |
| 同じロジックが 3 つ以上のモデルにある | マクロに抽出する |
| 1 つのモデルに結合が 5 つ以上ある | 中間モデルを作成する |
| 理解しにくい | わかりやすい名前の CTE を追加する |
| パフォーマンスが遅い | 並列化できるように分割する |
アンチパターン
- 下流への影響を確認せずにリファクタリングする
- 複数の変更を一度に行う
- リファクタリング後に出力が一致することを検証しない
- 早まって抽出する(3 回以上使われるまで待つ)
- 既存のテストを更新せずに失敗させる
コメント
Markdown 対応