← Blog
AI駆動開発品質管理Claude Codeテスト

AIが量産するコードの品質をどう担保するか — 200コンポーネントで運用する4層の品質ゲート

6 分で読める

このトピックを深掘りする

Claude Code活用

Claude Codeを使った大規模リファクタリング・CLAUDE.md設計・セッション管理の実践知

全4本中 4 本目。

このトピックの記事一覧

シリーズ全体の流れを見ながら、次に読む記事へ進めます。 初めての方はホームへ →

この記事で答える質問

AIにコードを量産させたとき、品質は何で担保すればいいか。このサイトは約200個(カタログ登録197個)のインタラクティブコンポーネントをAI駆動で開発・運用していて、その全部が同じ品質ゲートを通っている。この記事では、実際に運用している仕組みを構成ファイル名のレベルで公開する。

先に全体像を示す。ゲートは4層で、後の層ほど「人間の目」から遠い。

ゲート 止めるもの
1 pre-commitフック×4 機密漏洩・境界違反・生成物の手編集・AI臭文体
2 データ不変条件テスト 参照切れ・構造の破れ
3 ブラウザ実機スモーク 「ビルドは通るが描画されない」
4 品質スコア+PR提案 生成コンテンツの直接公開

ポイントは、どの層もLLMに「ちゃんとやって」と頼む方式ではないことだ。全部が決定論的なコードで、同じ入力には同じ判定を返す。

前提: 人間が全行レビューできない量をAIが書く

コンポーネント約200個のうち、人間が全行を読んだものはごく一部だ。AI駆動開発のスループットは人間のレビュー帯域を必ず超える。そこで「レビューを増やす」のではなく、「レビューしなくても壊れない構造」に投資した。

具体的には、品質の定義を機械が判定できる形に分解する。機密が漏れていないか、参照が切れていないか、実際に描画されるか、文体がAI臭くないか——これらは全部、コードで検査できる。

層1: コミットを止める4つのフック

.githooks/pre-commit は4つの検査を直列に実行し、どれか1つでも落ちればコミット自体が失敗する。

bun run scripts/repo-guard.ts
bun run scripts/check-llm-client-boundary.ts
bun run scripts/check-generated-files.ts
bun run scripts/humanizer-check.ts --staged

それぞれの役割は明確に分かれている。

  • repo-guard.ts — ステージされた差分に、私的メモのパスや op://(シークレット参照)が混ざっていないかを走査する。AIは作業中に参照した文脈を成果物へ写しがちで、公開リポジトリでは漏洩事故に直結する
  • check-llm-client-boundary.ts — LLM SDKと機密ファイルローダーを同じファイルでimportしたら無条件で拒否する。「機密データがLLM APIに流れうる経路」を静的に遮断する不変条件
  • check-generated-files.ts — sitemap・feed・llms.txtなどの生成物が、再生成コマンド以外の経路で変更されていたら止める。生成物の手編集は次の再生成で消える「時限バグ」になる
  • humanizer-check.ts — 後述

文体までゲートする — humanizer-check

一番変わっているのは4つ目だろう。humanizer-check.ts は日本語テキストの「AI特有の文体」を正規表現で検出する。実際のパターンの一部:

  • 「実現・構築・推進」系の漢語に「しており」が続く接続(AIが多用する構文)
  • 「今後の展開が注目され──」で締める定型文
  • 「非常に」「極めて」を「重要・高い」に重ねる過剰修飾
  • - **見出し:** 説明 形式の3連続(AIが好む箇条書きの癖)
  • 全段落が同じ書き出しで始まる均質構造

ブログ記事もAIが書くこのサイトでは、文体品質も「レビューで気づく」ではなく「機械が落とす」対象だ。ちなみにこの記事自体、このチェックを通過しないと公開できない。

層2: データの不変条件をテストとして固定する

コンテンツはすべて静的TypeScriptデータなので、構造の約束事をvitestの不変条件テストにしてある。例を挙げる。

  • 全ブログ記事はちょうど1つのトピッククラスタに属する(pillars.test.ts)— 0個でも2個でもテストが落ちる
  • 記事が参照するデモID・関連記事は、実在するカタログエントリに解決できなければならない(参照切れの検出)
  • dateModifieddate より過去であってはならない

AIに記事やコンポーネントを追加させると、登録漏れ・参照ミス・二重登録が一定確率で起きる。不変条件テストがあれば、それは「レビューで見つける間違い」ではなく「CIが赤くなる間違い」になる。間違いの発見コストが、人間の注意力から切り離される。

層3: 「描画された」をブラウザの実機で確認する

Canvasコンポーネントには特有の落とし穴がある。型が通り、ビルドが通り、Reactのレンダリングも成功して、画面が真っ黒というケースだ。ユニットテストでは捕まらない。

そこでPlaywrightのスモークテスト(tests/e2e/site-smoke.spec.ts)は、Canvasのピクセルを実際に読む。

const { data } = context.getImageData(0, 0, node.width, node.height)
// アルファ>0 かつ 輝度>0 のピクセルが一定数あるか数える

「Canvas要素が存在する」ではなく「何かが実際に描かれた」を検証する。あわせて、ページ読み込み中のconsole errorをすべて捕捉してゼロ件であることを要求し、横スクロールが発生していないこともチェックする。CIはlint→テスト→ビルド→このブラウザスモークの順で流れ、mainマージ後のデプロイまで自動化されている。

層4: 生成コンテンツは点数とPRで二段に止める

記事の自動生成パイプライン(scripts/engine/)には、コード用とは別の公開ゲートがある。

  1. 品質スコア: 生成記事は評価を受け、qualityScore >= 70 を満たさないものは公開対象にならない(AUTO_PUBLISH_MIN_SCORE = 70
  2. PR提案モード: 閾値を超えた記事も、mainへ直接pushされることはない。日次パイプラインはPRを作るところまでで止まり、公開は人間のマージ操作で初めて起きる

つまり「作成」と「公開」を分離してある。自動化が壊れたときの最悪ケースが「変なPRが1本立つ」に閉じ込められていて、本番には何も起きない。実際、この分離に助けられたことがある。生成パイプラインの登録ファイル出力に欠陥が見つかったときも、壊れた変更はPRの中に留まっていて、本番は無傷だった。

設計思想: ルールはプロンプトではなくフックに置く

4層に共通する原則は1つで、静的に検査できるルールをプロンプトに書かないことだ。

「機密パスを含めないで」「参照を切らさないで」「AIっぽい文体を避けて」とCLAUDE.mdに書いても、LLMは確率的にしか従わない。しかも指示が増えるほど1つあたりの遵守率は下がる。同じルールをフック・テスト・CIに置けば、遵守率は100%で、プロンプトは「機械で判定できない判断」だけに使える。

AIへの指示を増やすことと、AIの出力を検査する機械を増やすこと。量産体制で効くのは後者だった。

まとめ

  • AI量産コードの品質は、レビュー強化ではなく決定論的ゲートの多層化で担保する
  • 層1: pre-commitで機密・境界・生成物・文体を止める
  • 層2: データ構造の約束事は不変条件テストに固定する
  • 層3: 「描画された」はブラウザ実機のピクセルで確認する
  • 層4: 生成コンテンツは品質スコア+PR提案で「作成」と「公開」を分離する
  • 原則: 静的検査できるルールはプロンプトでなくコードに置く

量産の実録はピラー記事: Claude Codeで200コンポーネントを一括修正した実録に、セッション運用の規律は1セッション1タスクの原則にまとめている。

よくある質問

Q. AIが書いたコードは全部人間がレビューすべきですか?
量産体制では現実的ではありません。AI駆動開発のスループットは人間のレビュー帯域を超えるため、「レビューしなくても壊れない構造」への投資が必要です。機密漏洩・参照切れ・描画失敗・文体品質など、機械が判定できる性質に品質を分解し、フック・テスト・CIの決定論的ゲートに固定するのが現実解です。
Q. プロンプト(CLAUDE.md)に品質ルールを書くのではだめですか?
静的に検査できるルールをプロンプトに書くのは非効率です。LLMは指示に確率的にしか従わず、指示が増えるほど1つあたりの遵守率は下がります。同じルールをpre-commitフックやテストに置けば遵守率は100%になり、プロンプトは機械で判定できない判断だけに使えます。
Q. AIが書いた文章の「AI臭さ」も機械で検出できますか?
一定のパターンは正規表現で検出できます。定型の締め文・過剰修飾・特定の接続構文・箇条書きの癖・段落書き出しの均質さなど、AIが多用する文体には規則性があるためです。このサイトではhumanizer-checkというpre-commitフックが日本語テキストを走査し、検出されるとコミット自体が失敗します。
Q. Canvasコンポーネントの「画面が真っ黒」問題はどう検出しますか?
ブラウザ実機のスモークテストでCanvasのピクセルデータを実際に読み、アルファ値と輝度が0より大きいピクセルが一定数あるかを検証します。型チェック・ビルド・Reactのレンダリング成功だけでは「実際に描画されたか」は保証できないため、getImageDataによるピクセル検証が最後の砦になります。
Q. AIが自動生成した記事はそのまま公開されますか?
されません。生成記事は品質スコアの閾値(70点)を超えないと公開対象にならず、超えた場合もPull Requestの作成で止まります。本番への公開は人間のマージ操作で初めて起きる二段構えで、「作成」と「公開」を分離しています。