同じAIを使っているのに、なぜか他の人の方が良いコードを生成している。そんな経験はありませんか?

実は、AIから高品質なコードを引き出せるかどうかは、「何を指示するか」で99%決まります

この記事では、プロの開発者が実践している「AIを賢く使うプロンプトの書き方」を、初心者でもすぐに実践できる形で徹底解説します。読み終わる頃には、あなたのAIが別人のように賢くなっているはずです。

衝撃の事実:同じAI、指示の差で結果は100倍変わる

まず、実例をお見せします。同じAI(Claude 3.5 Sonnet)に、異なる指示を出した結果です。

❌ 悪いプロンプトの例

あなたの指示:

ログイン機能を作って

AIの出力:

// 超シンプルな、使い物にならないコード
function login(username, password) {
    if (username === "admin" && password === "password") {
        return true;
    }
    return false;
}

問題点:

  • パスワードが平文で比較されている(セキュリティ最悪)
  • データベースに接続していない
  • エラーハンドリングがない
  • 実際のアプリでは使えない

✅ 良いプロンプトの例

あなたの指示:

ログイン機能を実装してください。

【要件】
- フレームワーク: Express.js
- データベース: PostgreSQL
- 認証方式: JWT トークン
- パスワード: bcrypt でハッシュ化して保存
- ログイン失敗は3回まで、それ以上は30分間アカウントロック
- CSRF対策を含める
- エラーは適切なHTTPステータスコードで返す

【制約】
- async/await を使用すること
- エラーハンドリングは try-catch で囲むこと
- SQLインジェクション対策を含めること

AIの出力:

// 本番環境で使える、プロダクションレベルのコード
const express = require('express');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const { Pool } = require('pg');

const router = express.Router();
const pool = new Pool({ /* DB設定 */ });

router.post('/login', async (req, res) => {
    try {
        const { email, password } = req.body;
        
        // バリデーション
        if (!email || !password) {
            return res.status(400).json({ error: 'メールアドレスとパスワードを入力してください' });
        }
        
        // ユーザー取得(SQLインジェクション対策済み)
        const result = await pool.query(
            'SELECT * FROM users WHERE email = $1',
            [email]
        );
        
        if (result.rows.length === 0) {
            return res.status(401).json({ error: '認証情報が正しくありません' });
        }
        
        const user = result.rows[0];
        
        // アカウントロックチェック
        if (user.locked_until && new Date() < new Date(user.locked_until)) {
            return res.status(423).json({ error: 'アカウントがロックされています' });
        }
        
        // パスワード検証
        const isValid = await bcrypt.compare(password, user.password_hash);
        
        if (!isValid) {
            // ログイン失敗回数を増やす
            await pool.query(
                'UPDATE users SET failed_attempts = failed_attempts + 1 WHERE id = $1',
                [user.id]
            );
            
            // 3回失敗でロック
            if (user.failed_attempts + 1 >= 3) {
                const lockUntil = new Date(Date.now() + 30 * 60 * 1000);
                await pool.query(
                    'UPDATE users SET locked_until = $1 WHERE id = $2',
                    [lockUntil, user.id]
                );
                return res.status(423).json({ error: 'ログイン失敗が3回を超えました。30分後に再試行してください' });
            }
            
            return res.status(401).json({ error: '認証情報が正しくありません' });
        }
        
        // ログイン成功 - 失敗回数リセット
        await pool.query(
            'UPDATE users SET failed_attempts = 0, locked_until = NULL WHERE id = $1',
            [user.id]
        );
        
        // JWTトークン生成
        const token = jwt.sign(
            { userId: user.id, email: user.email },
            process.env.JWT_SECRET,
            { expiresIn: '24h' }
        );
        
        res.json({ 
            token,
            user: { id: user.id, email: user.email, name: user.name }
        });
        
    } catch (error) {
        console.error('Login error:', error);
        res.status(500).json({ error: 'サーバーエラーが発生しました' });
    }
});

module.exports = router;

結果の差は歴然です。指示の質が、出力の質を100%決定します。

プロンプトの「ゴルディロックス原則」:粗すぎず、細かすぎず

プロンプトには、**最適な粒度(細かさのレベル)**があります。

🔴 粗すぎる(Too Coarse)

指示:

Netflixのようなサイトを作って

結果:

  • 機能しないプレースホルダーだらけ
  • AIが「Netflix」の範囲を勝手に解釈(幻覚)
  • 使えないコード

🔴 細かすぎる(Too Fine)

指示:

2つの数値を受け取って足し算する関数を書いて。引数名はnumber1とnumber2で。戻り値の型はnumberで。関数名はaddNumbersで。

結果:

  • AIに頼む意味がない(自分で書いた方が速い)
  • トークンとお金の無駄

🟢 最適(Just Right)

指示:

ReactとTailwindを使って、再利用可能なビデオプレーヤーコンポーネントを作成してください。

【機能】
- ソースURLをpropsで受け取る
- 再生/一時停止ボタン
- 音量調整スライダー
- 再生時間表示
- バッファリング状態の表示

【デザイン】
- モダンでミニマルな見た目
- モバイル対応(レスポンシブ)

結果:

  • 実用的なコンポーネント
  • 必要な機能が全て含まれる
  • そのまま使える品質

原則: プロンプトは「仕事の原子単位」で切り分ける。1回の指示で、1つの完結した機能を作らせる。

【超重要】プロンプトの4層構造を理解する

プロ開発者のプロンプトは、**4つのレイヤー(層)**で構成されています。

レイヤー1:役割の割り当て(ペルソナ)

AIに「誰として振る舞うべきか」を教えます。

例:

あなたは10年以上の経験を持つシニアReact開発者です。パフォーマンス最適化とアクセシビリティに精通しています。

効果: AIのトレーニングデータの中で、「React」「パフォーマンス」「アクセシビリティ」に関連する部分を重点的に参照するようになります。

レイヤー2:目的(The "What")

何を作るのかを明確に述べます。

例:

ユーザープロフィール編集フォームを作成してください。

レイヤー3:制約条件(The "How")

ここが最も重要。どのように作るかを指定します。

技術スタック:

- フレームワーク: Next.js 14 (App Router)
- バリデーション: Zod
- フォーム管理: React Hook Form
- UI: shadcn/ui コンポーネント

スタイル・規約:

- TypeScript strict モード
- Airbnb JavaScriptスタイルガイドに従う
- 関数は必ず型注釈を付ける

パフォーマンス:

- 初期レンダリングは100ms以内
- 画像は Next/Image で最適化
- 不要な再レンダリングを防ぐ

セキュリティ:

- XSS対策を含める
- CSRF トークンを実装
- 入力値は必ずサニタイズ

レイヤー4:参照資料(グラウンディング)

特定のファイルやドキュメントを参照させます。

Cursorの場合:

@auth_controller.ts のエラーハンドリングパターンを参考にして、
@types/user.ts のUserインターフェースを使用してください。

Claude Codeの場合:

CLAUDE.md に記載されているプロジェクト規約に従ってください。既存の API エンドポイント(src/api/)の設計パターンを踏襲してください。

実践テクニック1:「思考の連鎖」でミスを激減させる

複雑なタスクの場合、いきなりコードを書かせてはいけません

❌ 悪い例

指示:

決済処理の実装をして

AIの動き:

[すぐにコードを書き始める]
→ エッジケースを見落とす
→ セキュリティホールが残る

✅ 良い例

指示:

決済処理を実装する前に、以下を行ってください:

1. 実装計画の概要を説明する
2. 必要なファイルの変更をリストアップする
3. 潜在的なエッジケースを特定する
4. セキュリティ上の考慮点を列挙する

計画が確認できたら、コードを書いてください。

AIの動き:

[Plan]
決済処理の実装には以下のステップが必要です:

1. ファイル変更
   - src/api/payment.ts を作成
   - src/types/payment.ts にインターフェース追加
   - src/services/stripe.ts を作成

2. エッジケース
   - ネットワークエラー時の処理
   - 決済途中でユーザーが離脱
   - 重複決済の防止
   - 金額が0円または負の値の場合

3. セキュリティ考慮点
   - APIキーは環境変数で管理
   - 決済金額はサーバー側で再計算(クライアント送信値を信用しない)
   - idempotency key の実装
   - ログに機密情報を残さない

この計画でよろしいですか?

あなた: 「OKです。実装してください」

結果: エッジケースとセキュリティを考慮した、堅牢なコードが生成される。

Claude Codeでは think コマンドを使用:

claude think "決済処理の実装計画を立てて"

実践テクニック2:スクリーンショット駆動開発

UIを作るとき、テキストで説明するのは非効率です。画像を使いましょう。

❌ テキストだけで説明(非効率)

指示:

ヘッダーを作って。左側にロゴ、右側にナビゲーションメニュー。背景は白で、文字は黒。ロゴは高さ40pxで、メニューは横並び。メニュー項目は「Home」「About」「Contact」。ホバー時に下線を表示。

結果: イメージと違うものが出来上がる可能性が高い。

✅ スクリーンショットを使用(超効率的)

指示:

[Figmaデザインのスクリーンショットを貼り付け]

この画像の通りにヘッダーを実装してください。
- Tailwind CSS を使用
- パディングとタイポグラフィを正確に一致させる
- モバイル(768px未満)ではハンバーガーメニューに変更

結果: デザイン通りのUIが1回で完成。

さらに強力:「バイブチェック」ループ

UIに問題がある場合:

  1. 壊れたUIのスクリーンショットを撮る
  2. チャットに貼り付けて「このレイアウトの問題を修正して」と指示
  3. 修正されたコードを確認

テキストで「divが左に10pxずれすぎている」と説明するより、10倍速いです。

実践テクニック3:コンテキスト・レイヤリング

AIに「全体像」を理解させることが重要です。

❌ コンテキストなしの指示

指示:

ユーザー一覧ページを作って

AIの思考:

「ユーザー」って何のユーザー?どんなデータ構造?既存のコードとの関係は?デザインシステムはある?

結果: 推測で作るため、既存コードと統合できない。

✅ コンテキストを含む指示

指示:

ユーザー一覧ページを作成してください。

【コンテキスト】
@types/user.ts に定義されたUserインターフェースを使用。
@components/Table.tsx の既存テーブルコンポーネントを再利用。
@api/users.ts から getUserList() でデータ取得。

【機能要件】
- ページネーション(1ページ20件)
- 名前とメールアドレスで検索
- ロール(Admin/User)でフィルタリング
- 「編集」「削除」ボタン

【デザイン】既存の管理画面デザインシステムに従う。

結果: 既存コードと完璧に統合された、一貫性のあるページが完成。

プロのテクニック:「コンテキストマップ」の作成

大規模プロジェクトでは、一時的なMarkdownファイルを作ります。

feature_context.md の例:

# 決済機能のコンテキスト

## 関連ファイル
- src/types/payment.ts - 決済関連の型定義
- src/services/stripe.ts - Stripe連携
- src/api/orders.ts - 注文API

## 使用するインターフェース
```typescript
interface Payment {
  id: string;
  amount: number;
  currency: string;
  status: 'pending' | 'completed' | 'failed';
}
```

## 既存のエラーハンドリングパターンすべてのAPI呼び出しは以下の形式:
```typescript
try {
  const result = await apiCall();
  return { success: true, data: result };
} catch (error) {
  logger.error(error);
  return { success: false, error: error.message };
}
```

使い方:

@feature_context.md を参照して、決済確認ページを作成してください。

これにより、プロジェクト全体を読み込ませることなく、必要な情報だけをピンポイントで提供できます。

プロンプト・テンプレート集(コピペOK)

テンプレート1:新規コンポーネント作成

あなたは経験豊富な[フレームワーク名]開発者です。

【タスク】
[コンポーネント名]を作成してください。

【技術スタック】
- フレームワーク: [例: React 18]
- スタイリング: [例: Tailwind CSS]
- 状態管理: [例: Zustand]

【機能要件】
1. [機能1の説明]
2. [機能2の説明]
3. [機能3の説明]

【制約条件】
- TypeScript strict モード
- Props の型定義は必須
- コメントは日本語で記述
- アクセシビリティ(ARIA属性)を考慮

【参照】
@existing-component.tsx のスタイルパターンを踏襲してください。

テンプレート2:バグ修正

以下のエラーが発生しています:

【エラーログ】

[エラーメッセージをペースト]


【コンテキスト】
@problematic-file.ts を分析して、問題の原因を特定してください。

【要求】
1. エラーの原因を説明する
2. 修正案を提示する
3. 修正後のコード全体を提供する
4. 同様のバグが他の箇所にないか確認する

エラーハンドリングを追加し、将来同じエラーが起きないようにしてください。

テンプレート3:リファクタリング

@target-file.ts をリファクタリングしてください。

【目的】コードの保守性とパフォーマンスを向上させる。

【制約】
- 既存の機能・動作は一切変更しない
- テストが通ることを確認
- 複雑度を下げる(if文のネストを減らす)

【実施項目】
1. 重複コードの削除
2. 関数を小さな単位に分割
3. 変数名をより分かりやすく変更
4. 型安全性の向上

変更内容の説明も含めてください。

テンプレート4:セキュリティレビュー

@api/ フォルダ内のすべてのファイルをセキュリティの観点でレビューしてください。

【チェック項目】
- SQLインジェクション対策
- XSS対策
- CSRF対策
- 認証・認可の適切な実装
- 機密情報のログ出力
- レート制限の実装

問題が見つかった場合:
1. ファイル名と行番号を指摘
2. 脆弱性の説明
3. 修正案の提示

問題がない場合も、その旨を報告してください。

よくある失敗パターンと対策

失敗1:曖昧な指示

❌ 悪い例:

エラーを直して

✅ 良い例:

以下のエラーを修正してください:
[エラーログをペースト]

@error-file.ts の該当箇所を特定し、修正案を提示してから実装してください。

失敗2:コンテキスト不足

❌ 悪い例:

データベースに保存して

✅ 良い例:

フォームデータを PostgreSQL に保存してください。

【テーブル】
users テーブル(@schema.sql 参照)

【使用するライブラリ】
pg (node-postgres)

【エラーハンドリング】一意制約違反は 409 Conflict を返すその他のDBエラーは 500 Internal Server Error

失敗3:一度に詰め込みすぎ

❌ 悪い例:

ユーザー登録、ログイン、プロフィール編集、パスワードリセット、メール認証を実装して

✅ 良い例:

【ステップ1】まずユーザー登録機能を実装してください。(詳細な要件)

完成したら、次のステップに進みます。

プロンプトの品質チェックリスト

指示を出す前に、このリストを確認しましょう:

  • 役割(ペルソナ)を指定したか?
  • 何を作るか明確に述べたか?
  • 技術スタックを指定したか?
  • 制約条件(スタイル、パフォーマンス、セキュリティ)を含めたか?
  • 参照すべきファイルを@で指定したか?
  • 複雑なタスクの場合、計画フェーズを含めたか?
  • UIの場合、スクリーンショットを添付したか?
  • 期待する出力形式を明示したか?

まとめ:プロンプトは「コードの設計図」

バイブコーディングにおいて、プロンプトはソースコードそのものです。

良いプロンプトの条件:

  1. 明確 - 曖昧さがない
  2. 具体的 - 技術スタックと制約を含む
  3. 構造化 - 4層レイヤーで整理
  4. 適切な粒度 - 粗すぎず細かすぎず

この記事で紹介したテクニックを実践すれば、あなたのAIは別次元の賢さを発揮します。

プロンプトの書き方を磨くことは、未来のプログラミングスキルそのものなのです。


📚 次に読むべき記事:

🔗 参考Web記事: