開発現場で生成AIツールを導入したものの、「AIが期待通りのコードを書いてくれない」「修正の手間がかかって逆に時間がかかる」といった悩みを抱えてはいませんか?
Devin(自律型の開発エージェント)とCursor(エディタ統合型のコーディング支援AI)については、それぞれ公式サイト(Devin / Cursor)で概要やドキュメントを確認できます。本記事では導入手順や料金の詳細には踏み込みませんが、すでに触れたことがある方が実務に落とし込みやすい内容になっています。
私の案件では、開発プロセスにおける「製造」および「単体テスト」工程でAI活用を進めましたが、単なる工数削減だけでなく、成果物の品質を均一化し、属人性を排除することが大きな課題でした。
そこで今回は、DevinやCursorを活用して手戻りを防ぎ、生産性を高めるための具体的な手法と実践ルールをご紹介します。
この記事を読むとAIへの指示の立て方(プロンプトの型)、単体テストで品質を担保する基準の与え方、バグ修正時に迷走させないコツを理解することができます。
目次
- 1.全体の流れ(フェーズのイメージ)
- 2.対象読者
- 3.AIで高品質なコードを生み出すためのベストプラクティス
- 4.実際に導入してみて分かった成果と注意点
- 5.導入して見えてきた課題と補足
- 6.まとめ
1.全体の流れ(フェーズのイメージ)
- 製造(共通ルール → Devin / Cursor それぞれのコツ)
- ↓
- 単体テスト(指標・観点の明示 → 再指示で補完)
- ↓
- コード修正(範囲限定・ログそのまま・ピンポイント指示)
- ↓
- (一貫して)人間による設計判断・レビュー
2.対象読者
- ・ 開発現場に生成AIを導入しているが、アウトプットの品質にばらつきを感じているエンジニア
- ・ Go言語のある程度の実装経験がある方
- ・ AIツール(Devin、Cursorなど)のより実践的な使い方を知りたい方
- 記事作成時点のバージョン情報
- Golang:1.24.13
- Cursor:2025年12月22日時点のバージョン
- Autoモードで使用
- Devin:2025年8月22日時点のバージョン
3.AIで高品質なコードを生み出すためのベストプラクティス
ここからは、実際に私の現場で定めているルールや、やってみて効果が高かった取り組みをフェーズごとにご紹介します。
【製造フェーズ 1】Devin・Cursorに共通して効かせたいルール
共通ルール① LLM(Geminiなど)を経由してプロンプトを作ってみる
人間が直接Devinに指示を書くと、どうしても背景情報が抜け落ちたり、曖昧になったりしがちではないでしょうか?
そこで、Devinへのプロンプトを人間が直接書かず、一度GeminiなどのLLMを経由して作成するというルールを設けました。たとえば「ユーザー登録機能を作って」と直接投げるのではなく、Geminiに対して要件定義書とDB設計を添えて、「Devinが迷わず実装できる詳細なプロンプトを作成して」と依頼し、生成結果をDevinに渡します。目的・背景・要件・制約が構造化され、エージェントが迷走するリスクを抑えられます。
プロンプト作成のイメージ(NG / OK)
- # NG(Devinに直接、情報が不足)
- ユーザー登録機能を作ってください。
- # OK(先にGemini等に依頼してプロンプトを起こす例)
- DevinにGo言語でユーザー登録APIを実装させたいです。以下の要件定義書とDB設計を根拠に、Devin向けの実装プロンプトを(目的・背景・エンドポイント・バリデーション・エラー方針・参照ファイル)が漏れないように構造化して作成してください。
共通ルール② プロンプトに「ペルソナ」を設定してみる
プロンプトの冒頭で、「あなたはシニアフルスタックエンジニアです」や「Go言語のクリーンアーキテクチャに精通したエキスパートです」といった役割(ペルソナ)を明示してみましょう。
AIの振る舞いを特定の専門領域に寄せることで、ライブラリの選び方やコードの書き方がプロジェクトの求める水準に揃いやすくなります。「Goで決済APIを書いて」だけだと抽象的になりがちですが、ペルソナまで含めると、期待する品質のイメージがAI側にも伝わりやすくなります。

【製造フェーズ 2】自律型AI(Devin)のプロンプト設計とKnowledge活用
Devin① 指示の粒度を整えてから分割してみる
「〇〇機能一式を作って」と大きすぎる指示を出していませんか?
AIが一度に処理できる情報量(コンテキストウィンドウ)には限界があります。一度に大量の指示を与えると、依存関係を見落としたり、事実と異なる内容をもっともらしく出力してしまう現象(ハルシネーション)が起きやすくなります。例として、存在しない関数やAPIを生成する場合があります。
ファイル単位・関数単位まで細分化してステップバイステップで指示を出してみましょう。
指示の分割の例
- # NG
- 決済機能のAPIとバッチ処理を全部実装して
- # OK
- まずは決済機能のドメインモデルとリポジトリ層(例: repository/payment.go)を実装してください。
- 完了したらサービス層の実装に移ります。
Devin② プロジェクト固有のルールは「Knowledge」として読み込ませる
開発スタックや独自のコーディング規約など、案件ごとの前提知識は「Knowledge(ナレッジ)」としてまとめ、Devinに事前に読み込ませています。これにより、毎回プロンプトで「この独自パッケージを使って」と指示する手間が省け、プロジェクトのルールを守りやすくなります。
【製造フェーズ 3】Cursorでコンテキストを揃え、PlanからAgentへ
Cursor① 背景情報を「過剰なほど」与えてみる
AIは、参照していないファイルの内容を推測して実装を進めようとします。人間にとっては自明なことでも、AIには関連ファイルやDB設計書などのインプット情報を明示的に参照させる必要があります。
Cursor② いきなり実装させず「計画(Planモード)」から始めてみる
Cursorなどを使用する際、いきなりコードを書かせるのではなく、まずはPlanモードで実装計画を立案させるのがおすすめです。
現在の情報で不足しているものがないかをAIに洗い出させ、懸念点(仕様の未確定部分など)を解消してから、人間が計画をレビューします。その後に実装させることで、コードの再現性が高まりやすくなります。
【単体テストフェーズ】カバレッジと観点を明示してAIにテストを書かせる
単体テスト① 具体的な指標値と観点を提示してみる
「単体テストコードを書いて」という曖昧な指示では、正常系のテストを1つ書いて満足してしまうAIも少なくありません。
そこで、目標カバレッジや網羅の観点を明示します。当プロジェクトではチーム合意のもと「75%以上」を目標としました(レガシー比率やリスク許容度によって妥当な数字は変わるため、自チームの基準に置き換えてください)。C0(命令網羅)とC1(分岐網羅)に加え、正常系だけでなく境界値・異常系も含める、と具体的に指示します。
単体テスト指示の例
- # 以下の要件に従って単体テストコードを作成してください。
- - 目標カバレッジ: 75%以上(※プロジェクトの基準に合わせて変更可)
- - 網羅観点: C0(命令網羅)とC1(分岐網羅)を満たすこと
- - テストケース: 正常系に加え、すべての境界値および異常系パターンも作成すること
単体テスト② 足りない部分は「再指示」で補完する
AIが書いたテストコードにパターンの漏れがあった場合、ゼロから書き直させるのは非効率です。AIは既存のコード構造を維持したまま追記するのが得意なので、「〇〇の境界値パターンが不足しているから追加して」とピンポイントで指摘して修正させましょう。
実コード例(Go / 境界値テスト)
本例ではIsAdult(age)について、18歳以上を成人(true)と定義します。
① 初回のAI出力(IsAdultの単体テストを書いてのみ依頼した想定)
表形式のループまでは出てきているが、閾値18をまたぐテストケースにない状態です。(コメントはあとから読みやすくするための注釈です。)
func TestIsAdult(t *testing.T) {
tests := []struct {
age int
want bool
}{
// 【不足】正常系1件のみ。age=18 の「直前・ちょうど・直後」がなく、C1(分岐の両側)を検証できない
{age: 20, want: true},
}
// 以下は初回から問題ないことが多い(再指示の対象にしなくてよい)
for _, tt := range tests {
got := IsAdult(tt.age)
if got != tt.want {
t.Fatalf("age=%d: got=%v, want=%v", tt.age, got, tt.want)
}
}
}② 追記させたときの再指示プロンプト例
既存の TestIsAdult はそのままにして、tests スライスのケースだけ修正してください。
IsAdult は「18歳以上なら true」。不足している境界値テストを追加してください。
具体的には age=17 で false、age=18 と age=19 で true になるケースを、
既存のテーブル駆動テストのパターンに合わせて3件追加してください。
他の関数やファイルは変更しないでください。
③ プロンプトで追加された内容(tests の { … } 内に入った行)
再指示の結果、不足していたのはスライス内のテーブル行だけで、次の3行が追加されます(既存の {age: 20, want: true} の前に挿入する、後ろに足す、など並びはAI出力次第で構いません)。
// tests のリテラル内に追記された行(プロンプトによる追加部分のイメージ)
{age: 17, want: false}, // 境界の直前(18未満 → false)
{age: 18, want: true}, // 境界そのもの(18以上 → true)
{age: 19, want: true}, // 境界の直後(18より上も true)④ 再指示後のコード全体(完成形のイメージ)
境界3件に加え、初回の正常系1件も残した例です。
func TestIsAdult(t *testing.T) {
tests := []struct {
age int
want bool
}{
{age: 17, want: false}, // 境界値の直前
{age: 18, want: true}, // 境界値そのもの
{age: 19, want: true}, // 境界値の直後
{age: 20, want: true}, // 初回からあった正常系(そのまま残した例)
}
for _, tt := range tests {
got := IsAdult(tt.age)
if got != tt.want {
t.Fatalf("age=%d: got=%v, want=%v", tt.age, got, tt.want)
}
}
}初回の指示から「境界値・異常系まで」と書いておくと、この手の追記は不要になりやすいです。すでに薄いテストが出てきたときは、初回コードをそのまま貼り、足りない行をプロンプトで指定すると、ゼロから書き直さずに補完しやすくなります。
【コード修正フェーズ】バグ修正でAIを迷走させないテクニック
バグ修正① 修正範囲を具体的に限定してみる
AIに「修正して」とだけ伝えると、意図しないリファクタリングまで実施してしまい、新たなバグを生むことがあります。
「指定した関数以外は絶対に変更しないこと」「特定のディレクトリ以外は触らないこと」など、修正範囲を具体的にプロンプトに組み込んで制限をかけましょう。
修正範囲を限定する指示の例
- # 制約事項
- - 修正範囲以外のコードは変更しないでください
- - 指定した関数以外は変更しないでください
- - 例: src/code/ ディレクトリ以外は変更を加えないでください
バグ修正② エラーログは「そのまま」投げてみる
人間がエラーの内容を要約して伝えると、AIにとって重要な情報が欠落する可能性があります。「データベース接続エラーが出た」と意訳するのではなく、スタックトレースや出力されたエラーログをそのまま貼り付けて投げる方が、発生箇所と原因の特定が速くなることが多いです。
エラーログをそのまま渡す例
- # 以下のエラーが発生しました。原因と修正案を提示してください。
- 2024/12/01 10:00:00 [error] failed to connect to database: dial tcp 127.0.0.1:3306: connect: connection refused
- at main.initDB (main.go:45)
- at main.main (main.go:12)
バグ修正③ 原因が分かっているならピンポイントで指示する
もし人間側で調査して原因が特定できているなら、AIに推測させる余地を与えないことが重要です。「〇〇の計算結果が間違っているから直して」ではなく、「当該ファイルの〇行目にある定数をAからBに変更して。それ以外は触らないで」と具体的に指示した方が、副作用の少ない修正がしやすくなります。
ピンポイント指示の例
- # NG
- 消費税の計算結果が間違っているようです。ロジックを確認して直してください。
- # OK
- src/service/tax_calculator.go の 25行目にある const TaxRate = 0.08 を 0.10 に変更してください。
- それ以外は変更しないでください。
4.実際に導入してみて分かった成果と注意点
得られた成果
これらのルールを実践した結果、インプット情報を網羅的に与えることで、期待するレベルのコードを安定して生成しやすくなりました。
特にLLMを経由したプロンプト生成や、事前計画(Planモード)での合意形成を挟むことで、AIが意図しないコードを生成し、手戻りが発生する頻度は、導入前と比べて体感で大きく減りました(効果の大きさは案件・タスクにより異なります)。
また、最も効果を感じたのは単体テストのコード生成です。人間が書くと数時間かかることもある境界値パターンの網羅を、AIなら短時間で、かつ考慮漏れの少ないテストケースとして展開しやすくなりました。
5.導入して見えてきた課題と補足
一方で、AI任せにしてはいけないポイントや、今後の課題も見えてきました。
AIの提案を評価する「設計力・レビュー力」の重要性
AIが提示した計画やコードを鵜呑みにせず、正誤を見極めるエンジニア自身の技術力がより一層求められるようになります。経験の浅いメンバーがAIの成果物を評価するのは難しいため、サポート体制の構築が必要です。
仕様の「行間」が読めない
現時点のモデルでは、仕様書の矛盾の指摘や、文脈にない意図の推測は難しい場合が多いです。そのため、人間側で情報を整理してインプットする必要があります(例:Excelの設計書はAIが読み込みづらいことがあるため、Python等でMarkdownやCSVに変換して読み込ませる工夫など)。
モデルの特性を理解して使い分ける
使用するモデルによって得意分野やアウトプットの品質・傾向が変わります。モデル名やバージョンの更新は速いため、公開時点の公式情報と、自チームでの比較検証を基に、タスクに応じて選ぶことが重要です。
6.まとめ
本記事で扱った主なポイントは次のとおりです。
- ◦ プロンプトはLLM経由で構造化し、ペルソナで品質の期待値を揃える
- ◦ Devinは指示を細かく分割し、Knowledgeでプロジェクト前提を共有する
- ◦ Cursorはコンテキストを過剰なくらい渡し、Plan → 人間レビュー → Agentの順で実装する
- ◦ 単体テストはカバレッジ・C0/C1・境界値/異常系など観点を数値・文章で明示し、不足は再指示で足す
- ◦ 修正は範囲を限定し、ログは要約せず貼り、原因が分かっているときは一行単位で指示する
正直、最初は「AIに任せれば早く終わるのでは」と期待しがちですが、指示や情報が足りないと手戻りが増えることもあります。プロンプトの型や計画の挟み方、テストの基準など「こちら側の型」をそろえると、結果のブレは小さくなりやすい、というのが私の実感です。人間が設計とレビューで軸を持ち、AIが下書きや網羅を担う役割分担がはっきりすれば、生産性も品質も上がりやすくなります。
まずはここから試してみてほしいこと
- 既存のDevin/Cursor用プロンプトを、一度Gemini等に「構造化・漏れチェック」させてから投げ直してみる
- Cursorの次の実装タスクで、いきなり書かせずPlanモードで不足情報を洗い出させてから実装をする
- 単体テストの指示に、目標カバレッジとC0/C1・境界値/異常系を一文で必ず添える
- バグ修正ではスタックトレースをそのまま貼り、変更してよいファイル・関数をプロンプトで囲う
この記事が、これからAIと一緒に開発を進める方のひとつの参考になればうれしいです。
参考リンク