AI時代にも、静的解析は必要です。
理由は、コードを書く速さが上がっても、正しさを確認する仕事は残るからです。機械的に確認できることを静的解析に任せれば、AIと人は仕様や設計の判断に集中できます。
この記事では、静的解析の全体像と、AI開発での役割を整理します。特定のAI製品は前提にしません。後半のコード例にはTypeScriptを使います。
静的解析は何をするものか
静的解析は、対象のプログラムを実行せず、コードの構造や型、データの流れから問題を調べる技術です。
目的は、実行前に見つけられる問題を早く見つけ、同じ基準で繰り返し確認することです。
代表的な範囲は次の3つです。分類は重なり、製品ごとに対応範囲も異なります。
| 種類 | 主に確認すること | 例 |
|---|---|---|
| Lint | 不審な記述やルール違反 | 未使用の変数、誤りやすい書き方 |
| 型検査 | 値と操作の整合性 | 未定義になり得る値への操作、引数の型違い |
| データフロー解析など | 分岐や値の流れに沿った問題 | 外部入力が危険な処理へ届く経路 |
Lintにも型情報を使うものがあります。セキュリティ向けの静的解析はSASTと呼ばれますが、静的解析全体がセキュリティ専用というわけではありません。
たとえばCodeQLは、データの流れをモデル化して解析します。ただし、実行時に決まる呼び出し先など、正確に扱いにくいケースもあります。CodeQLのデータフロー解析の説明
また、整形を行うフォーマッターや、依存ライブラリの既知の脆弱性を調べる検査も開発に役立ちますが、ここでは役割を分けて考えます。
AIがコードを読めても、残す価値がある
AIにもコードの問題を探してもらえます。それでも、静的解析を残す理由があります。
ルールとして明文化できる確認を、毎回AIに推論させる必要がないからです。
| 観点 | 静的解析を組み合わせる意味 |
|---|---|
| 再現性 | 同じコード・設定・ツールバージョンで判定を揃えやすい |
| 速度と費用 | 定型的な検査を、LLMの生成処理を介さず実行できる |
| レビュー | 人が同じ種類のミスを繰り返し指摘する負担を減らせる |
| 運用 | チームのルールを設定として保存し、CIで継続して確認できる |
ただし、重い解析には時間がかかります。ツールの実行費用や設定の保守も必要です。静的解析なら常に安い、速いとは限りません。
AIが書いたコードにも、人が書いたコードにも、同じ検査を適用する。これだけで、コードの作成者に依存しない確認手順を作れます。
トークン効率は「AIに何を渡すか」で変わる
ここでいうトークンは、AIが入出力する文章やコードを処理する単位です。
静的解析を先に実行すると、「全体を読んで問題を探して」という依頼を、「この診断の原因を確認して直して」という依頼に絞れる場合があります。
AIに渡す情報の例は、次のとおりです。
- ファイル名と行番号
- ルールIDやエラーコード、診断メッセージ
- 該当箇所と判断に必要な周辺コード
- 守るべき仕様と、期待する動作
たとえばESLintは診断をJSONで出力できます。その結果から必要な項目を抽出すれば、AIへ渡す情報を整理できます。JSONにするだけで短くなるわけではありません。ESLintの出力形式
この方法で、問題を探すための読み込みや、修正の往復が減る可能性があります。ツール実行型のAIでも、検査結果を会話へ戻す部分にはトークンを使うため、出力の整理は必要です。
逆に、次の運用では消費が増えることもあります。
- 大量の警告や重複したログを、毎回すべて渡す
- 周辺情報を削りすぎて、AIが見当違いの修正を繰り返す
- 軽微な警告を1件ずつ依頼し、同じコードを何度も読み込ませる
したがって、静的解析を入れれば必ずトークンが減るとは言えません。本記事では削減率を測定していません。
効果を測るなら、同程度の修正課題で、完了までの入力・出力トークン、往復回数、所要時間、修正後の品質を比較します。料金を見る場合は、使うモデルやキャッシュの条件も揃えます。
見るべきなのは、1回の入力の短さより、正しい修正に到達するまでの総量です。
短いコードで分かる、検査と判断の違い
次のコードは、名前から前後の空白を取り除きます。
function displayName(name: string | undefined): string {
return name.trim();
}
しかし、nameにはundefinedも入ります。TypeScriptのstrictNullChecksは、こうした値を区別して検査します。以下では、この検査を含む--strictを指定します。TypeScriptのstrictNullChecks
上のコードをbefore.tsに保存して実行します。Node.jsとnpmが必要です。
npx --yes --package typescript@5.9.3 tsc --strict --noEmit before.ts
確認した診断の要点は、次のとおりです。
error TS18048: 'name' is possibly 'undefined'.
ここまでは、型検査が判断できます。
一方、未入力の名前をどう扱うべきかは、仕様の問題です。エラーにするのか、入力を求めるのか、代わりの表示を使うのか。型検査だけでは決まりません。
「未定義、空文字、空白だけの名前は『名無し』と表示する」という仕様なら、次のように直せます。
function displayName(name: string | undefined): string {
return name?.trim() || "名無し";
}
このコードをafter.tsに保存し、同じ条件で検査すると通ります。
npx --yes --package typescript@5.9.3 tsc --strict --noEmit after.ts
2026年10月4日、macOS、Node.js v26.0.0、TypeScript 5.9.3で確認しました。修正前はTS18048で終了コード2、修正後は診断なしで終了コード0でした。これは型検査の確認であり、製品仕様の妥当性を検証したものではありません。
AIに修正を任せるときも、この仕様を渡す必要があります。型エラーが消えたことと、期待した動作になったことは、別々に確かめます。
導入は小さく、確認は繰り返す
最初から全ルールを有効にする必要はありません。まず、言語の型検査と、実害が明確なLintルールから始めます。必要な範囲にセキュリティ検査を加えます。
AIと組み合わせる手順も、シンプルです。
- コードを変更したら、静的解析を実行する。
- 診断と必要な文脈、仕様をAIに渡して修正する。
- 同じ静的解析を再実行し、関連するテストも行う。
- 人が仕様や設計上の判断を確認する。
解析を通すためにルールを無効化したり、型の検査を回避したりしていないかも確認します。
既存の警告が多い場合は、既知の警告を記録し、新しい問題から対応する運用も考えられます。ただし、重大な既知の問題を放置する理由にはしません。
AIへ渡す診断を絞ることと、解析対象を絞ることも別です。変更行だけの解析では、呼び出し元や依存先への影響を見落とす場合があります。
静的解析に任せる範囲を決める
静的解析には誤検知も見逃しもあります。通過しても、バグがないことや安全であることの保証にはなりません。
業務要件、使いやすさ、実際の環境での性能などは、テストや計測、人の確認も必要です。AIは仕様の整理や修正案の検討を助けられますが、その提案も検証します。
AI時代に静的解析は必要か。答えは、必要です。
機械的に確かめられることは静的解析へ。実行して確かめることはテストへ。何を正しいとするかは、人がAIも活用して決める。
この役割分担を作ることが、トークンだけでなく、開発全体の手戻りを減らす出発点になります。