prompt-engineering-production-code.html
< BACK 夜間の薄暗い開発者のデスク、光るモニター、散らばったメモ、冷えたコーヒーマグが物憂げな編集調の照明に照らされている

本番コード向けプロンプト・エンジニアリング:厳しい教訓

木曜日の夜11時43分のことだった。GPT-4が完全な自信を持って生成した340行のReactコードを眺めていた。見た目は整理されている。コメントもしっかりある。本番環境では完全に壊れていた。カスタムフックがサイレント再レンダリングループを引き起こす方法で状態を管理していた。エラーを吐かないタイプだ -- 単に静かにパフォーマンスを殺すだけで、金曜日の朝にクライアントから「チェックアウトページが9秒かかるんだけど何で?」と電話が来るまで気づかない。

その夜は、YouTubeチュートリアルやTwitterのスレッドではどんなものよりも、プロンプト・エンジニアリングについて教えてくれました。そしてあの夜以来、同じような夜を何度も経験しています。

ウェブ開発は9年間やってきた。Seahawk Media では12,000以上のサイトをデプロイした -- WordPress、ヘッドレスビルド、カスタムReactアプリ、相応の取引量を扱うWooCommerceストア。AIコーディングアシスタントは2023年初頭に本格的にワークフローに入り、その後は「奇跡的だ」と思ったり「ラップトップをテムズ川に投げ込みたい」と思ったりの繰り返しだ。Seahawk Media we've shipped well over 12,000 sites -- WordPress, headless builds, bespoke React apps, WooCommerce stores handling serious transaction volume. AI coding assistants entered my workflow properly around early 2023, and I've swung between thinking they're miraculous and wanting to throw my laptop into the Thames.

実際に学んだことはこれです。辛い方法でね。

---

モデルはあなたのコードベースを知りません。あなたが教える必要があります。

これは当たり前に聞こえます。実際にはそうではありません。

開発者が犯す最大の誤りは -- 最初の6ヶ月の自分を含め -- LLMを既にコード全体を読んだシニアエンジニアのように扱うことだ。「ユーザー認証を処理する関数を書いて」と聞けば、真空状態では技術的に正しいものが返ってくる。しかしあなたのプロジェクトはFirebaseではなくSupabaseを使っている。トークンはlocalStorageではなくhttpOnlyクッキーに入っている。エラー形式は { status, message, data } であって、モデルがデフォルトで使ったものではない。Supabase, not Firebase. Your tokens live in httpOnly cookies, not localStorage. Your error format is { status, message, data }, not whatever the model defaulted to.

モデルは間違っていません。単にあなたのことを知らないだけです。

プロジェクトプリアンブルを毎回与える

現在、意味のあるコーディングセッションのたびに、私が「コンテキストブロック」と呼ぶもので開始しています。書くのに約90秒かかります。こんな感じです:

  • スタック:Next.js 14(App Router)、TypeScript、Supabase、Tailwind CSS 3.4Next.js 14 (App Router), TypeScript, Supabase, Tailwind CSS 3.4
  • ステート:Zustand、Reduxはどこにもなし
  • 認証:ミドルウェア経由のhttpOnly cookieを使用したSupabase Auth
  • エラー形式:{ success: boolean, error?: string, data?: unknown }{ success: boolean, error?: string, data?: unknown }
  • スタイリング規約:ユーティリティファースト、絶対に必要な場合を除きカスタムCSSファイルなし

些末でないリクエストの前にそれを貼り付ける。Cursor では _context.md ファイルをプロジェクトルートに保持することでこれをしている。2キーストロークで貼り付けられる。出力品質は目に見えて向上する -- 仮定は減り、引っ張がす必要がある部分が減る。Cursor by keeping a _context.md file in the project root. Two keystrokes to paste. The output quality jumps noticeably -- fewer assumptions, fewer things I have to rip out.

---

具体性がすべてだ

私がまだAIを本格的に使う前の2022年、クライアントから文字通り2文のブリーフを受け取りました:「予約システムを構築してくれ。質の高いものをお願いします。」スコープについて3週間にわたって議論を重ねました。その経験は私に強く印象付けられ、今の私のプロンプト作成方法に直結しています。

曖昧なプロンプト → 曖昧なコード。毎回です。

「オーダーを取得する関数を書いて」というプロンプトはそれなりの結果をくれます。一方「TypeScriptの非同期関数fetchOrdersByUserを書いて。userId: stringを受け入れ、Supabaseのordersテーブルでuser_idが一致しstatusがcancelledではない行をクエリし、created_atの降順でソートし、Order[]を返すか型付きエラーをスローする」というプロンプトは、実際に本番にリリースできるものをくれます。fetchOrdersByUser that accepts a userId: string, queries the orders table in Supabase where user_id matches and status is not cancelled, orders results by created_at descending, and returns Order[] or throws a typed error" will get you something you can actually ship.

違いはモデルの能力ではありません。プロンプトの具体性です。

コードプロンプトに含めるべきもの

  1. 関数名と署名 -- モデルに命名規則を作らせない -- don't let the model invent naming conventions
  2. 入力型と出力型 -- 必要に応じてTypeScriptジェネリクス -- TypeScript generics if relevant
  3. データソース -- どのテーブル、どのAPIエンドポイント、どのキャッシュレイヤー -- which table, which API endpoint, which cache layer
  4. すでに認識している境界ケース -- 「配列が空の場合を処理する」など -- "handle the case where the array is empty"
  5. やってはいけないこと――「useEffect をここに使うな。代わりにサーバーアクションを使え」 -- "don't use useEffect for this, use a server action"

この最後の点は、人々が認識している以上に重要だ。モデルに何を避けるべきかを伝えることで、膨大な時間が節約できる。私は今、プロジェクトごとに小さな「アンチパターン」ノートを保管し始めた――「ユーザーの操作が必要な場合以外、クライアントコンポーネントを使うな」といった類のものだ――そして、そのプロジェクト用のプロンプトに関連する行を含める。

---

プロンプトをチェーンさせる。一度にすべてを求めないこと。

Seahawk は 2023 年後半、fintech クライアントを抱えていた――誰とは言わないが――複数ステップの KYC フローを構築していた。複雑な案件だった。ドキュメントアップロード、生体認証チェックの統合、ステータスポーリング。初期段階で、GPT-4 に「KYC フロー全体のコンポーネントを構築してくれ」と頼む過ちを犯した。600 行の一見勇敢に見えるゴミが出てきた。ロジックが絡み合い、関心事が混在し、UI の状態とビジネスロジックの間に実質的な分離がなかった。

だから私はそれを破棄して、チェーンで再スタートした。

最初のプロンプト: 「4 ステップの KYC フローの状態マシンを設計してくれ。ステップ: 本人確認、ドキュメントアップロード、ライブネス、レビュー。状態型とトランジションだけくれ、UI は不要。」"Design the state machine for a 4-step KYC flow. Steps: identity, document upload, liveness, review. Give me the state type and transitions only, no UI."

2番目のプロンプト:「この状態マシン[貼付]が与えられたとき、Zustandストアを書いてください」"Given this state machine [paste], write the Zustand store."

3番目のプロンプト:「このストア[貼付]が与えられたとき、StepIdentityコンポーネントを書いてください。このステップだけです」"Given this store [paste], write the StepIdentity component. Just this step."

チェーンアプローチからの出力は使える状態だった。完璧ではなかった――まだ約 30% 書き直した――が、使える状態だった。モノリシックアプローチは何も与えてくれなかった。

Anthropic自身のプロンプティングに関するガイダンスは、複雑なタスクをサブタスクに分割することを推奨しており、正直なところ、試行錯誤を通じて私が見つけたものと完全に一致しています。コードベースを壊す前に、問題を細分化してください。 talks about breaking complex tasks into subtasks, and honestly, this aligns exactly with what I found through trial and error. Break the problem down before you break your codebase.

---

自分自身と議論させる

これは完全な偶然で発見したものです。生成されたユーティリティ関数をレビューしていたとき、単に実行するのではなく、フォローアップのプロンプトを追加しました:「あなたが書いたコードの潜在的なバグやエッジケースは何ですか?」"What are the potential bugs or edge cases in the code you just wrote?"

モデルは 3 つの問題を見つけたが、それらはまだ考慮されていなかった。そのうちの 1 つは本当の問題だった――非同期ループでの競合状態で、本番環境でのデバッグは悪夢になるところだった。

今は定期的にこれをやっている。コードを書いて、それからモデルにコードを批評してもらう。それからそれを修正してもらう。少し不毛に感じられる――モデルに自分の仕事をレビューさせるなんて――が、一貫して、苦痛なデバッグセッションの後にしか気づかないような問題が浮かび上がる。

これをもっと進めることができます。動作する関数を手に入れたら、次のように試してください:「パフォーマンスに焦点を当てて書き直してください」または「これは高い並行実行下でどのように動作するでしょうか?」答えが常に適用可能とは限りませんが、約40%の場合、実行する価値のあるものが浮かび上がります。"Rewrite this with a focus on performance" or "How would this behave under high concurrency?" The answers aren't always applicable, but about 40% of the time they surface something worth acting on.

---

「ロール + 制約」フレーム

今は常に使っているプロンプトパターンがあるが、1年目に気づいていれば良かったと思う。それは「あなたは[特定のエンジニアのタイプ]です。制約は[ハードルール]です。では[タスク]を」という形だ。"You are a [specific type of engineer]. Your constraint is [hard rule]. Now [task]."

例えば:「あなたはデータベースのクエリ効率を深く気にするバックエンドエンジニアだ。制約として、このレンダリングのために必要以上のものをフェッチできない――オーバーフェッチは禁止だ。管理ダッシュボード用の Supabase クエリを書け。注文数、総売上、最近の 5 件の注文を返すこと。」"You are a backend engineer who cares deeply about database query efficiency. Your constraint is that you cannot fetch more than what's needed for this render -- no over-fetching. Write a Supabase query for the admin dashboard that returns order count, total revenue, and the five most recent orders."

このフレーミングは 2 つのことをする。モデルの「ペルソナ」を、実際に私が必要としているものと合わせる。そして制約は保護柵として機能する――モデルが生成するときに明示的に自分自身に対してチェックするもの。

OpenAIのプロンプト作成ベストプラクティスでは、明示的な指示を伴うペルソナをモデルに与えるという同様の考え方が説明されている。読む価値があるが、正直なところ制約の部分は彼らのドキュメントでは強調が不足していると言える。 describe a similar idea around giving the model a persona with explicit instructions. Worth reading if you haven't, though I'd say the constraint piece is underemphasised in their docs.

そのフレーム付きプロンプトの出力を「管理ダッシュボードのためのSupabaseクエリを書いてください」と比較してみろ。全く違う。本当に。

---

プロンプトをやめて、ただコードを書くべき時

これは誰も声に出して言いたくない部分だ。

AI コーディングツールは以下の点で優れています:ボイラープレート、CRUD操作、ユーティリティ関数、既に書いたコードのテスト作成、フォーマット間の変換(JSON スキーマから TypeScript 型へ、SQL から Supabase クエリへなど)、そして大幅に修正することになるものの初期ドラフト作成。

一方、以下の点で著しく不得意です:アプリの実際のアーキテクチャを理解すること、あなたの特定のスケールにとってどのトレードオフが重要かを知ること、複雑なステートフルなインタラクションに関わるものを大幅なガイダンスなしで書くこと、そして仕様が本質的に曖昧な場合のすべてのもの。

私は今、個人的なルールを持っています:コードを正しく書き直すために4回以上のフォローアッププロンプトを送った場合、チャットを閉じて自分で書きます。プロンプトデバッグの時間コストは、特に約50行以下のものについて、単に書いてしまう時間コストを超える可能性があります。

Stack Overflow Developer Survey 2024 は、開発者の 76% が AI ツールを使用しているか、使用予定であると判明した――だが同じデータは精度に対する比較的低い信頼を示していた。この使用と信頼のギャップこそが、優れたプロンプトエンジニアリングが存在する場所だ。Stack Overflow Developer Survey 2024 found that 76% of developers are using or planning to use AI tools -- but the same data showed relatively low trust in accuracy. That gap between usage and trust is exactly where good prompt engineering lives.

---

コードをバージョン管理するのと同じようにプロンプトをバージョン管理する

昨年から、AI アシスタンスが重要なプロジェクトに prompts/ フォルダを保持し始めました。Markdown ファイル。主要な機能領域ごとに1つ。プロンプトが特に良い出力を生成するとき、保存します。より良いバージョンを見つけたら、ファイルを更新します。prompts/ folder in projects where AI assistance is significant. Markdown files. One per major feature area. When a prompt produces particularly good output, I save it. When I find a better version, I update the file.

執着的に聞こえるかもしれない。最後の大きなプロジェクトだけで――Shopify から移行している小売業者向けのヘッドレス WooCommerce ビルド――おそらく 6 時間節約させてくれた。プロダクトクエリプロンプトを再利用した(軽微な編集を加えて)4 つの異なるコンポーネント全体で、一から文脈を再エンジニアリングする代わりに。

Gitで管理する。本気で。プロンプトの品質は、入力を使い捨てではなく成果物として扱えば再現可能だ。LangChainのプロンプトテンプレートはフレームワークの文脈でこの考えを体系化しているが、フレームワークは不要だ。ほとんどのエージェンシーワークフローではMarkdownファイルのフォルダで十分だ。LangChain's prompt templates formalise this idea in a framework context, but you don't need any framework -- a folder of Markdown files is enough for most agency workflows.

---

よくある質問

プロンプトエンジニアリングは本当に応用可能なスキルなのか、それとも単にモデル固有のものなのか?

ほぼ転用できる。コア原則——具体性、文脈設定、チェーニング、批評ループ——はGPT-4、Claude 3.5 Sonnet、Gemini、次に来るものすべてに当てはまる。構文は多少異なり、モデルによっては特定のフレーミングへの反応が良い場合もあるが、根底のロジックは変わらない。私の経験ではClaudeは明示的な制約に反応しやすく、GPT-4は例に反応しやすい。細かな違いはあるが、基本は同じだ。syntax varies a bit and some models respond better to certain framing, but the underlying logic holds. I've found Claude tends to respond well to explicit constraints; GPT-4 responds well to examples. Minor differences, same fundamentals.

コードレビューではAI生成コードをどのように扱うか?

他のコードと同じだ。本番運用に入るなら、レビューを受ける。以上だ。SeahawkではPRで「AIが生成した」というフラグを立てるのをやめた。それがレッドニシンになってしまい、レビュアーが別の基準で審査するようになったからだ。時に不公正に、時に不十分に。コードは自分の価値で判断される。私がフラグを立てるのは、ロジックが明白でなく、推論を説明するインラインコメントを追加していないセクションだけだ。

システムプロンプトを使うのか、それともチャットプロンプトだけなのか?

両方だ。Cursorでは.cursorruleファイルに永続的なプロジェクトレベルの指示を入れている——そうでなければ毎回貼り付けるようなものだ。ChatGPTやClaudeのWeb UIで単発タスクをやるなら、すべてチャットに入れる。.cursorruleアプローチは反復作業を大幅に削減し、長いセッションでもモデルの一貫性が保たれる。.cursorrules file for persistent project-level instructions -- things I'd otherwise paste every time. For one-off tasks in the ChatGPT or Claude web UI, it's all in the chat. The .cursorrules approach has meaningfully reduced repetition and the model stays more consistent across a long session.

AI が開発者に取って代わるというあなたの本当の見方は?

開発者を置き換えているのではなく、特定のタスクを置き換えている。判断——何を作るか、どう設計するか、このクライアントの実際の状況にどのトレードオフが適合するか——こうしたものはまったく自動化できない。むしろ、圧力を受けているのは純粋に実行志向だった開発者で、設計やアーキテクチャ志向の開発者ではない。判断層を磨け。それが防衛可能な部分だ。tasks, not developers. The judgment calls -- what to build, how to architect it, which trade-off fits this client's actual situation -- those are nowhere near automated. If anything, the developers getting squeezed are the ones who were purely execution-oriented, not design or architecture-oriented. Sharpen the judgment layer. That's the defensible bit.

---

ウェブ上でものを作り続けて9年間、同じ教訓が何度も繰り返されている。出力の質は入力の質で決まる。それはクライアントのブリーフだった時代も真実だった。プロンプトの今でも真実だ。the quality of your output is determined by the quality of your inputs. That was true when it was client briefs. It's true now with prompts.

そのモデルは高速で、時々素晴らしく、頻繁に自信過剰なジュニア開発者だ。それに応じて管理する必要がある。

< BACK