Claude Media
Claude Codeでdbt build --selectを回す — 影響範囲だけ検証する開発手順

Claude Codeでdbt build --selectを回す — 影響範囲だけ検証する開発手順

dbt Coreのモデル開発をClaude Codeに任せるとき、dbt build --selectとstate:modified+で影響範囲だけを検証させる手順と、実行コマンドの権限設計をまとめます。

dbt Coreのモデル開発をClaude Codeに任せるなら、検証コマンドはdbt build --selectに絞るのが基本です。プロジェクト全体を毎回ビルドするとウェアハウスの実行時間も費用も膨らみます。変更したモデルとその下流だけを、テスト込みでDAG順に回せば足ります。この記事では、選択構文の使い分け、state:modified+による差分検証、Claude Codeに渡す指示と権限設定までを手順にします。

dbt buildが1回で実行する範囲

dbt buildは、モデルの実行、テスト、スナップショット、シードの投入を、選択したリソースについてDAG順に行うコマンドです。dbt v1.11以降はユーザー定義関数のビルドも含みます。

実行結果は単一のmanifestと単一のrun resultsにまとまります。モデル、テスト、シード、スナップショットの結果が1ファイルに入るため、Claudeに結果を読ませる入口も1つで済みます。

動作として押さえる点は3つです。

  • 上流のテストが失敗すると、下流のリソースは実行されずスキップされる
  • dbt build -s model_aはmodel_aを実行し、直接依存するテストも走らせる。選択外の別の親にも依存するテストをどう扱うかは、後述の--indirect-selectionで変わる
  • フラグはrun・test・snapshot・seedと共通で、--full-refreshはモデルにもシードにも同じ値で効く

1つ目が、Claudeに回させる理由です。model_aのuniqueテストが落ちれば、model_aに依存するmodel_bはビルドされません。壊れたデータを下流へ流さずに止まります。テストを警告扱いにすればスキップは起きませんが、検証としては弱くなります。

--selectの書き方を使い分ける

選択の評価順は、選択メソッド(tag:など)、グラフ演算子(+など)、集合演算子の順です。最後に、現在のタスクが扱うリソース種別以外が除かれます。

開発中によく使う形を並べます。引数は引用符で囲む書き方が推奨されています。

dbt build --select "my_model"          # このモデルとその直接テスト
dbt build --select "my_model+"         # このモデルと下流すべて
dbt build --select "+my_model"         # このモデルと上流すべて
dbt build --select "my_model+1"        # このモデルと直接の下流だけ
dbt build --select "path:marts/finance,tag:nightly"  # 両方を満たすもの

+を付ける位置で意味が変わります。後ろに付けると下流、前に付けると上流です。3+my_model+4のように段数も指定できます。カンマは空白なしでつなぎ、すべての条件を満たすリソースだけを選びます。

選択範囲は実行前にdbt lsで確かめる

構文が複雑になるほど、意図と違う範囲を選びやすくなります。選択結果を実行せずに確認するコマンドがdbt lsです。Claudeには、ビルドの前に必ずdbt lsを打たせると安全です。

dbt ls --select "my_model+"

出てきた一覧が想定より多ければ、+1で段数を絞るか、--excludeで外します。

どのテストが一緒に走るかは--indirect-selectionで決まる

dbt build --selectでは、選んだモデルに関係するテストも自動で選ばれます。どこまで含めるかを決めるのが--indirect-selectionで、モードは4つあります。

  • eager(既定): 選択したノードを親に持つテストを、選択外の親も参照するテストまで含めて選ぶ。選択外の親が開発環境に未構築だと、そのテストはエラーになる
  • buildable: 選択したノードとその祖先だけを参照するテストに絞る。モデルとその直接の祖先を突き合わせるテスト(集計値が入力と一致するか、など)も残る
  • cautious: 参照先がすべて選択範囲に入っているテストだけを走らせる
  • empty: テストを一切走らせない

差分検証では、未構築の上流に引っ張られた失敗を避けるため、cautiousが候補になります。--deferでも同じ問題を避けられます。

dbt build --select "state:modified+" --indirect-selection=cautious --state prod-run-artifacts

後述するrelationshipsのように複数の親を持つテストは、eagerだと選択外の親を巻き込みます。どのモードで回すかをCLAUDE.mdに決めておくと、Claudeの検証結果がぶれません。

state:modified+で変更したモデルだけを検証する

モデルを直した後に「自分が触った範囲」を機械的に選ぶ方法が、stateメソッドです。比較対象のmanifest(本番の実行結果など)を--stateで渡すと、それとの差分から対象を選びます。

  • state:new: 比較用manifestに同じunique_idのノードがない
  • state:modified: 新規ノードに加え、既存ノードの変更を含む
  • state:modified+: 変更ノードとその下流
# 比較用manifestはtarget/とは別のディレクトリに置く
dbt ls --select "state:modified+" --state prod-run-artifacts
dbt build --select "state:modified+" --state prod-run-artifacts

dbt buildの前にdbt lsを挟めば、Claudeが変更した範囲と、その下流に波及する範囲を先に一覧で確認できます。

差分検証で踏みやすい落とし穴

公式の注意事項から、Claudeに運用させる前に決めておく点を3つ挙げます。

  • --stateと--target-pathを同じパスにしない。dbtは解析時にmanifest.jsonを上書きするため、比較元が消えて差分が取れなくなる。比較用manifestはstate/のような専用ディレクトリへ移す
  • state:modifiedは、コードと設定の変更だけを検出する。ソースに新しい行が入ったことは検出しないので、定期実行ジョブには向かない。CIやマージ前の検証向けの選択子
  • 新しいテストだけを足したときは、テスト対象の上流モデルが開発環境に存在しないことがある。この場合は--deferで本番側の定義に解決させる

tagsとmetaの変更は変更扱いになりません。タグだけ付け替えた修正はstate:modifiedに現れないので、Claudeに「差分が空だった」と報告されても、タグ変更だけの修正なら正常です。

deferで上流を作り直さずに検証する

開発用スキーマに上流モデルがないと、下流モデルだけを実行してもエラーになります。--deferと--stateを併用すると、選択外かつ開発環境に存在しないrefは、比較用manifest側(本番)の定義に解決されます。

dbt build --select "state:modified+" --defer --state prod-run-artifacts

公式の例では、開発スキーマdev_aliceにmodel_aがないとき、model_bのref('model_a')が本番スキーマのprod.model_aに解決されています。ただし本番データを読むため、開発側に入れていた行数制限が効かない場合があります。relationshipsのように複数の親を持つテストは、環境をまたいで走る点にも注意が要ります。

失敗したテストだけを再実行する

直前の実行結果から、失敗したものだけを選び直すこともできます。ビルド結果は--stateで渡すディレクトリの成果物を参照します。

# 失敗したテストに関わるモデルを、1つ上流まで含めて再実行
dbt build --select "1+result:fail" --state path/to/artifacts

result:failはテストだけを選びます。テストには下流ノードがないため、result:fail+としても失敗したテスト自身しか返りません。モデルまで戻したいときは1+result:failの形にします。

Claude Codeに渡す指示

検証の型を決めたら、CLAUDE.mdに規約として書いておくと反復が安定します。次は断片の一例です。パスやディレクトリ名は自分のプロジェクトに合わせます。

## dbtの検証ルール
- モデルを編集したら、まず `dbt ls --select "state:modified+" --state state/` で範囲を確認する
- 確認後に `dbt build --select "state:modified+" --defer --state state/` を実行する
- `dbt build` を引数なしで実行しない(全体ビルドは禁止)
- 未構築の上流を参照するテストで落ちたら `--indirect-selection=cautious` を付けて再実行する
- 失敗したら出力のエラーを読み、`1+result:fail` で再実行する
- `--full-refresh` は人間の承認なしに付けない

ポイントは、Claudeに「実行して出力を読み、直して再実行する」ループを回させることです。dbt buildの出力には、テストごとのPASSやERRORと、最後の件数(PASS=7 WARN=0 ERROR=0 SKIP=0 TOTAL=7の形式)が出ます。ERRORとSKIPが0になるまで繰り返す、と指示すれば完了条件が明確になります。

dbtコマンドの権限を絞る

もう1つ決めておきたいのは、Claudeが実行してよいコマンドの範囲です。Claude Codeの権限ルールは、拒否、確認、許可の順に評価されます。許可ルールで拒否ルールの例外を作ることはできません。次の設定は、検証に必要なコマンドだけを許可し、全件の再作成を拒否する例です。

{
  "permissions": {
    "allow": [
      "Bash(dbt ls *)",
      "Bash(dbt compile *)",
      "Bash(dbt build *)"
    ],
    "deny": [
      "Bash(dbt * --full-refresh)",
      "Bash(dbt * --full-refresh *)"
    ]
  }
}

末尾に*を置く場合、直前のスペースがルールの一部になります。--full-refreshが行末に来る場合と途中に来る場合の両方を拒否するため、2行に分けています。

Bashルールの限界

Bashのルールは、Claudeが書いたコマンドの文字列に対して照合されます。/usr/bin/curlやsh -c '...'の形で呼ばれると、同じ拒否ルールをすり抜けます。この点は明記されており、ルールは「セキュリティ境界ではない」とされています。

ウェアハウス側の権限設計はこの限界への備えになります。開発用の接続先には、本番スキーマへの書き込み権限を与えないロールを使います。Claudeがどんな形でコマンドを呼んでも、権限のないロールでは書き込みが失敗します。deployを承認制にする考え方は、SAMでLambdaを開発する記事と同じ構造です。コマンドの許可と拒否を二重に組む考え方は、Denoのdeno taskと--allowでも扱っています。

--emptyで読み取りを省いた事前確認

dbt build --emptyは、refとsourceを0行に制限してモデルのSQLを実行します。入力データの高価な読み取りを避けつつ、依存関係とSQLがビルドできるかを確かめられます。Claudeが大きく書き換えたモデルを、最初に軽く通す用途に向きます。

dbt build --select "state:modified+" --empty --defer --state state/

--emptyでもモデルのSQL自体はターゲットのウェアハウスに対して実行されます。接続先の権限は上の設計どおりに絞っておきます。

ユニットテストを先に走らせる

ユニットテストを定義しているプロジェクトでは、dbt buildの処理順が次のようになります。

  1. SQLモデルに対するユニットテストを実行する
  2. ユニットテストが通ったら、モデルをマテリアライズする
  3. 実体化したモデルに対するデータテストを実行する

ユニットテストが落ちればモデルは実体化されないため、ウェアハウスの消費を抑えられます。--select "test_type:unit"やtest_type:dataで種類を指定すれば、開発中はユニットテストだけを回すこともできます。ロジックの修正をClaudeに任せるときは、先にユニットテストを書かせ、通るまで繰り返させると、ウェアハウスに触れる回数を減らせます。

よくあるつまずき

  • 選択結果が空になる: --stateの指す先にmanifest.jsonがない、またはtarget/と同じパスを指している。専用ディレクトリに退避してから比較する
  • 選択外のモデルを参照するテストがエラーになる: 既定のeagerが、未構築の親を持つテストまで選んでいる。--indirect-selection=cautiousか--deferで避ける
  • 下流のテストがSKIPになる: 上流のテストが失敗している。SKIPの原因は、最初のERRORを出したリソースにある
  • 変更したのにstate:modifiedで選ばれない: 変えたのがtagsかmetaだけの可能性がある。メタデータ扱いで、変更とは見なされない
  • 環境ごとに変わる値で誤検出が出る: v1.9以降ではstate_modified_compare_more_unrendered_valuesの動作フラグをtrueにして抑えられる。この場合、比較用のstateはv1.9以上で作り直す

dbt MCPサーバーを使うと、同じbuildやlistをMCPツールとして呼び出せます。接続手順はdbt MCPサーバーの使い方にあります。この記事は、MCPサーバーを入れずにCLIとCLAUDE.mdだけで回す構成です。メトリクスへの問い合わせはdbt Semantic Layerの使い方で扱っています。

まとめ

Claude Codeにdbtモデルを開発させるときは、全体ビルドを禁止し、dbt lsで範囲を確認してからdbt build --select "state:modified+"を回させる形が基本です。上流の再構築が重いときは--defer、軽く通したいときは--empty、失敗の再実行は1+result:failを使います。その上で、コマンドの許可はClaude Code側のルールで、書き込みの可否はウェアハウスのロールで二重に絞ります。

この記事を共有:XはてブLinkedIn