AIに開発を任せる前に整えた5つの安全装置 ─ 非エンジニアが実践する品質管理の仕組み
AIに開発を任せる前に整えた5つの安全装置 ─ 非エンジニアが実践する品質管理の仕組み
「AIに開発を任せて大丈夫なの?」
これ、よく聞かれます。
僕は非エンジニアですが、Claude Code(Anthropic公式のAIプログラミングCLI)を使って開発をしています。
でも、「AIに丸投げ」しているわけではありません。
何を作るか考えるのは人間の仕事。どんな体験を提供したいか、どんな価値を生み出すか。その構想を練り、方向性を決め、最終的に「これでいい」と判断するのは、あくまで僕たち人間です。
むしろ、自分がコードを書けないからこそ、品質を「仕組み」で担保する必要がありました。
今日は、僕が実際に整えた「5つの安全装置」を、設定ファイルやコード例とともに公開します。
この記事でわかること
この記事では以下を解説します:
- なぜ「仕組み」で品質を担保する必要があるのか
- 僕が実際に使っている5つの安全装置の詳細
- 各安全装置の具体的な設定ファイルとコード例
- 「任せっぱなし」と「仕組みで管理」の違い
特にエンジニアの方には、実際の設定内容が参考になると思います。
技術スタックの全体像
まず、使っている技術スタックを紹介します:
Frontend: Next.js 14 (App Router) + TypeScript + Tailwind CSS + shadcn/ui
Backend: Supabase (PostgreSQL + Auth + RLS + Edge Functions)
AI Tool: Claude Code (Anthropic公式CLI)
Testing: Playwright (E2E) + Vitest (Unit)
CI/CD: GitHub Actions + Vercel
VCS: GitHub
この構成で、以下の5つの安全装置を運用しています。
なぜ「仕組み」が必要なのか
AIは優秀です。でも、完璧ではありません。人間のプログラマーでもミスをするように、AIもミスをします。
大企業の開発チームには、コードレビュー、テスト、品質管理の専門チームがいます。でも、僕のような少人数の会社には、そんなチームはいません。だから、「仕組み」でカバーすることにしました。
人がチェックできないなら、自動でチェックする仕組みを作ればいい。
この発想で、以下の5つの安全装置を整えました。

安全装置① テストの2層構造(自動+人間)
何をしているか
テストは2層構造で行っています。
第1層:自動テスト(Playwright E2E)
「ボタンをクリックしたら正しく動くか」「フォームに入力したらエラーが出ないか」
こういった操作をPlaywrightで自動テストしています。機能を実装したら、Claude Codeがテストコードを書き、実行します。
playwright.config.ts の設定
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./tests",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
["html"],
["json", { outputFile: "test-results/results.json" }]
],
use: {
baseURL: process.env.BASE_URL || "http://localhost:3000",
trace: "on-first-retry",
screenshot: "only-on-failure",
},
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "webkit", use: { ...devices["Desktop Safari"] } },
{ name: "Mobile Chrome", use: { ...devices["Pixel 5"] } },
{ name: "Mobile Safari", use: { ...devices["iPhone 12"] } },
],
});
テストコード例(認証フロー)
import { test, expect } from "@playwright/test";
test.describe("認証フロー", () => {
test("ログイン → ダッシュボード遷移", async ({ page }) => {
await page.goto("/login");
await page.fill("[name=email]", process.env.TEST_USER_EMAIL!);
await page.fill("[name=password]", process.env.TEST_USER_PASSWORD!);
await page.click("button[type=submit]");
// ダッシュボードへの遷移を確認
await expect(page).toHaveURL(/.*dashboard/);
await expect(page.locator("h1")).toContainText("ダッシュボード");
});
test("未認証ユーザーはログインページにリダイレクト", async ({ page }) => {
await page.goto("/dashboard");
await expect(page).toHaveURL(/.*login/);
});
test("不正なパスワードでエラー表示", async ({ page }) => {
await page.goto("/login");
await page.fill("[name=email]", "test@example.com");
await page.fill("[name=password]", "wrongpassword");
await page.click("button[type=submit]");
// エラーメッセージの表示を確認
await expect(page.locator("[role=alert]")).toBeVisible();
});
});
ポイント
| 設定 | 効果 |
|---|---|
screenshot: "only-on-failure" |
失敗時のみスクリーンショット保存 → 原因特定が容易 |
retries: process.env.CI ? 2 : 0 |
CI環境ではリトライ2回 → flaky test対策 |
trace: "on-first-retry" |
リトライ時にトレース記録 → デバッグに活用 |
| 複数プロジェクト設定 | クロスブラウザ+モバイル対応の品質担保 |
第2層:人間による最終確認
自動テストに合格しても、必ず人間が最終確認します。
実際に、僕は毎回「人間向けテスト計画書」を作成し、自分の目で確認しています。自動テストでは見つけられない「使い勝手」や「違和感」は、人間の目でしか見つけられません。
テスト結果は記録に残し、問題があれば「改修ログ」として対応内容を全て文書化しています。
なぜ2層構造なのか
自動テストは「明確な基準」のチェックは得意ですが、「使い勝手」や「違和感」は見つけられません。
人間の目による確認と、フィードバックを漏れなく対応する仕組みがあってこそ、品質が担保できます。

安全装置② セキュリティ監査の多層防御
何をしているか
セキュリティは3つのレイヤーで守っています。
レイヤー1:Row Level Security(RLS)
SupabaseのRLSを使って、データベースレベルでアクセス制御しています。
-- テナント分離ポリシー
-- ユーザーは自分の組織のデータのみアクセス可能
CREATE POLICY "tenant_isolation" ON customers
FOR ALL
USING (
organization_id = (
SELECT organization_id FROM profiles
WHERE id = auth.uid()
)
);
-- 自分のプロフィールのみ参照可能
CREATE POLICY "own_profile_only" ON profiles
FOR SELECT
USING (auth.uid() = id);
-- 管理者は全データ参照可能
CREATE POLICY "admin_full_access" ON customers
FOR ALL
USING (
EXISTS (
SELECT 1 FROM profiles
WHERE profiles.id = auth.uid()
AND profiles.is_admin = true
)
);
-- 公開データは誰でも参照可能
CREATE POLICY "public_read" ON articles
FOR SELECT
USING (published = true);
なぜRLSが重要か
| アプローチ | リスク |
|---|---|
| アプリ側のみで制御 | バグやAPI直接アクセスでデータ漏洩の可能性 |
| DB側で制御(RLS) | どのルートからアクセスしても制御が効く |
アプリのバグがあっても、RLSが最後の砦として機能します。これはSupabaseを選んだ大きな理由の1つです。
レイヤー2:認証基盤のセキュリティ
Supabase Authには以下のセキュリティ機能が組み込まれています:
-JWT(JSON Web Token): 署名付きトークンによる認証 -ブルートフォース攻撃対策: パスワード総当たり攻撃への防御 -セッション管理: 自動トークンリフレッシュ -OAuth対応: Google、GitHub等の外部認証
レイヤー3:ホスティングのセキュリティ
Vercelを使用することで:
-自動HTTPS化: 全通信の暗号化 -環境変数の安全管理: シークレットの暗号化保存 -DDoS対策: エッジでの防御 -自動セキュリティヘッダー: XSS、クリックジャッキング対策
レイヤー4:AIによるセキュリティ監査
コードを変更するたびに、Claude Codeが自動でセキュリティチェックを実行します。
CLAUDE.mdに以下を設定:
## 🛡️ セキュリティ監査(自動実行)
**以下のコードを変更したら、自動でセキュリティチェックを実行:**
| 対象 | チェック内容 |
|------|-------------|
|**認証・ログイン**| 認証バイパス、セッション管理、パスワードハッシュ |
|**API Routes**| 入力バリデーション、SQLインジェクション、認可チェック |
|**フォーム入力**| XSS(サニタイズ)、CSRF対策 |
|**ファイルアップロード**| ファイルタイプ検証、パストラバーサル |
|**環境変数**| シークレット漏洩チェック、.gitignore確認 |
|**RLS(Supabase)**| ポリシー設定漏れ、権限昇格リスク |
なぜ多層防御なのか
1つの対策では穴が生じます。RLS + 認証基盤 + ホスティング + AI監査の組み合わせで、セキュリティを担保しています。

安全装置③ 危険操作のブロック(CLAUDE.md設定)
何をしているか
「データを全部消す」「本番環境を強制上書きする」といった危険な操作をブロックしています。
Claude CodeにはCLAUDE.mdというプロジェクト設定ファイルがあり、ここで許可・禁止操作を定義できます。
CLAUDE.mdの設定例
## 権限設定
### 確認なしで実行OK
- ファイル操作(Read/Edit/Write/Glob/Grep)
- Bashコマンド(npm, git status, git add, git commit等)
- MCP(Supabase, GitHub)の読み取り操作
### 確認が必要な操作
- `git push` - リモートへのプッシュ
- `vercel --prod` - 本番デプロイ
- データベースのDELETE/DROP操作
### 絶対禁止(システムで拒否)
- `rm -rf /` `rm -rf C:` 等のシステム破壊
- `git push --force origin main/master` - 強制プッシュ
- `DROP DATABASE` - データベース削除
## 🚨 作業スタイル
### 確認が必要な場合(限定的)
- UI/UXの重要な判断(レイアウト、色、操作フロー)
- ビジネスロジックの判断(料金、権限、制限)
- 復旧不可能な危険操作
### 確認不要(自律的に進める)
- 技術的な実装方法、ライブラリ選定、コード構造
- エラー修正・デバッグ・リファクタリング
- テスト作成・実行
settings.json での設定
Claude Codeのsettings.jsonでも制御できます:
{
"permissions": {
"allow": [
"Bash(npm:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Edit",
"Write",
"Read"
],
"deny": [
"Bash(rm -rf /)",
"Bash(git push --force origin main)",
"Bash(DROP DATABASE)"
]
}
}
なぜブロックが必要か
「取り返しのつかないミス」を防ぐためです。どんなに優秀でも一瞬の判断ミスは起こりえます。それを仕組みで防いでいます。
安全装置④ Git履歴による復元
何をしているか
すべてのコード変更をGitHubで履歴として保存しています。
機能1:変更履歴の完全記録
# 変更履歴の確認
git log --oneline -20
# 出力例
a1b2c3d feat: ログイン機能を追加
e4f5g6h fix: バリデーションエラーを修正
i7j8k9l refactor: コンポーネントを分割
「いつ、誰が、どんな変更をしたか」を全て記録。Claude Codeが行った変更も全て追跡可能です。
機能2:いつでも復元
# 特定のコミットに戻す
git revert a1b2c3d
# ブランチ全体を巻き戻す
git reset --hard HEAD~3
問題が起きたら、いつでも「変更前の状態」に戻せます。
GitHub Actionsによる自動化
.github/workflows/ci.yml でPRごとに自動チェック:
name: CI
on:
pull_request:
branches: [main]
jobs:
lint-and-type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npm run lint
- run: npm run type-check
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test
env:
BASE_URL: ${{ secrets.BASE_URL }}
TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}
security-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm audit --audit-level=high
自動チェック項目
| チェック | 内容 |
|---|---|
| Lint | ESLintによるコード品質チェック |
| Type Check | TypeScriptの型エラー検出 |
| E2E Test | Playwrightによる統合テスト |
| Security Audit | npm auditによる脆弱性スキャン |
なぜ履歴管理が必要か
「元に戻せる」安心感があるからこそ、積極的に改善を進められます。失敗を恐れすぎると、開発スピードが落ちてしまいます。
安全装置⑤ MCPによる外部サービス連携
何をしているか
Claude Codeは MCP(Model Context Protocol) を通じて、SupabaseやGitHubに直接アクセスできます。
.mcp.json の設定
{
"mcpServers": {
"supabase": {
"command": "npx",
"args": ["-y", "@supabase/mcp-server"],
"env": {
"SUPABASE_ACCESS_TOKEN": "${SUPABASE_ACCESS_TOKEN}"
}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
MCPで可能になること
| サービス | 機能 |
|---|---|
| Supabase | SQLクエリ実行、テーブル確認、RLS設定、型生成 |
| GitHub | PR作成、Issue管理、コードレビュー、ブランチ操作 |
型安全性の自動確保
Supabase MCPを使うと、DBスキーマから型を自動生成できます:
npx supabase gen types typescript --project-id $PROJECT_ID > types/database.types.ts
生成される型:
export interface Database {
public: {
Tables: {
customers: {
Row: {
id: string;
name: string;
email: string;
organization_id: string;
created_at: string;
};
Insert: { /* ... */ };
Update: { /* ... */ };
};
};
};
}
これにより、DBスキーマとTypeScriptの型が常に同期され、型エラーがコンパイル時に検出されます。
なぜMCPが重要か
AIがデータベースの状態を直接確認しながら開発できるため、「動かしてみたら動かない」という問題が激減します。
「任せっぱなし」と「仕組みで管理」の違い
ここまで読んで、「結局、全部AIがやってるんじゃないの?」と思うかもしれません。
確かに、コードを書く作業はAIがやっています。
でも、「何を作るか」を考えるのは人間です。お客様の課題は何か、どんな機能があれば解決できるか、どんな体験を提供したいか。この構想を練る部分は、AIには任せられません。
そして、「どんなチェックを、どのタイミングで、どう実行するか」を決めているのも僕です。品質の基準を設定し、それを監視する仕組みを設計するのは人間の役割。AIは、その設計に従って実行しているに過ぎません。
さらに、最終的な確認も人間が行います。自動テストでは見つけられない「違和感」や「使いにくさ」は、実際に触ってみないとわかりません。
❌ 任せっぱなし 「AIに言ったらなんかできた。中身はよくわからないけど動いてるからOK」
✅ 仕組みで管理 「品質基準を決めて、その基準を満たしているかを自動でチェックする仕組みを作る」
後者のほうが、長期的に安定した品質を保てます。

まとめ:人間が考え、仕組みが実行し、人間が確認する
今日紹介した5つの安全装置は、すべて**「仕組み」として機能**しています。ただし、その仕組みを設計し、最終判断を下すのは人間です。
| 安全装置 | 仕組み | 人間の役割 |
|---|---|---|
| テスト2層構造 | Playwright自動テスト | 最終確認、UX判断 |
| セキュリティ多層防御 | RLS + Auth + AI監査 | ポリシー設計 |
| 危険操作ブロック | CLAUDE.md設定 | 許可/禁止の決定 |
| Git履歴 | 変更記録、GitHub Actions | 復元判断 |
| MCP連携 | 型生成、DB同期 | 連携設計 |
非エンジニアでも、こうした仕組みを整えることで、安心して開発を進められます。
技術相談について
AI開発の導入や技術選定でお悩みの方は、お気軽にご相談ください。設定ファイルのテンプレートや、導入サポートも行っています。
- コーポレートサイト: https://llc-quest.com
- お問い合わせ: https://llc-quest.com/contact
- X (Twitter): https://x.com/questceo_ai
#ClaudeCode #AI開発 #Playwright #Supabase #セキュリティ #TypeScript


