🍣

AI駆動開発で苦労した話 〜笑えないけど笑うしかない日々〜

2025/03/07に公開8

AI駆動開発で苦労した話 〜笑えないけど笑うしかない日々〜

はじめに

※本記事で使用している画像はAIツール(DALL-E)を使用して生成したものです。

略歴と、SecureNaviにおけるAI駆動開発の導入の経緯

2002年にお茶の水のジュンク堂でXP本に出合い強烈な衝撃を受けました。2005年にはSeleniumに出合いさらに大きな衝撃を受けました。
以降、テストの自動化、コードやテストの自動生成をライフワークとしてエンジニア人生を送っているしがないエンジニアです。

前々職の富士通時代は、頑固な職人的なエンジニアの理想である「ダース・ベイダー」(ピープルウエアで有名の受け売り)になることを目指して日々精進していました。「ダース・ベイダー」って富士通じゃ・・・

変な前置きはこれぐらいにして、プログラミング言語を切り替える毎に統合開発環境を切り替えるのはストレスが大きいです。
そのため数年前から「JetBrains」の「All Products Pack」を利用していました。「GitHub Copilot」も「JetBrains」系のIDEで利用可能になったタイミングから使い始め、非常に使いやすい環境だと感じていました。

そんな中、2024年になって、Xで「プログラミングを勉強するなら統合開発環境はCursor一択でしょ!」みたいな呟きをよく見かけるようになりました。

これはちょっと触ってみないとなー、と思ってIntelliJ使いはどうしたらいいのでしょうか・・・と呟いたところ、きのぴーさんから返信いただいてやっぱり触って試さないと!「IntelliJ IDE」使いとして覚悟を決めました。

この時点ではComposerがリリースされて直ぐぐらいでしたが、これは凄いと思い、SecureNaviでも業務でトライアルしようということになりました。3名分アカウントを付与してもらい試行を開始しました。

個人利用と業務でCursorを利用し、やはり素晴らしいということで、2024年の10月からSecureNaviを開発しているプロダクト開発部の全エンジニアにライセンスを付与してもらいました。

その後、11月頃から自分がSecureNaviの3つ目の新規プロダクトの立ち上げ開発を行うことになりました。
これはAI駆動開発のトライアルに最適だと考え、AI駆動開発を全面的に取り入れました。
生成AIが生成したコードの割合は99.9%以上で、開発生産性としても2倍以上の値が出せたという結果になりました。

利用したモデルは99%以上「Claude 3.5 Sonnet」です。

今回は、SecureNaviの3つ目のプロダクトの新規立ち上げ開発時にAI駆動開発を行った「笑えない出来事」を「笑える内容」に昇華した記事です。
完璧ではないAIとうまく仕事を進めるための対策についてもお伝えします。

技術スタックとAI駆動開発で目指した事

AIエージェントを活用している方の共通の悩みとして、仕様や実現したい事をAIエージェントに伝えすぎると、コンテキストが溢れてしまいます。
その結果、意図した内容の物が生成されなくなります。

この事象を回避するため次の対策を考えました。

  • 関心の分離を適切に行いコンテキストを小さく保つ
  • 世間一般に知られている知識を用いることでコンテキストを広げる必要をなくす
    • AIエージェントが知っている知識を最大限に活かして良い感じに暴走してくれることを意識する

世間一般に知られている知識との観点の延長線上にあるのですが、クリーンアーキテクチャ的な意味でドメイン駆動設計を本格的にやってみたいという希望もありました。
「ドメイン駆動設計とAI駆動開発で新規プロダクトを実装し、SecureNaviが掲げる『悲報をなくす』というビジョンを早期に実現するための基盤とする」という目的で開発をスタートしました。

そんなこんなで無事に今年の2月にファーストリリースが行えました。今回は、そんなこんなの珍道中的な内容です。

技術スタック

  • バックエンド: Kotlin, Spring Boot, jOOQ, Ktlogger, Kotest, MockK
  • フロントエンド: Next.js, Tailwind CSS, shadcn/ui, Vitest
  • IDE: Cursor, Intellij, WebStorm -> IntelliJ IDE使いの苦悩・・・
  • E2E: Playwright
  • データベース: MySQL
  • マイグレーションツール: Flyway

なんでやねん!と叫ぶ毎日の珍騒動

1. とにかく低姿勢!

自分が悪くなくても「申し訳ございません」から会話が始まるのです。

2. 生成AIが短期記憶しか持たないことによる悲劇

真っ先に直面した課題が、生成AIがなんでもすぐ忘れるという悲劇です。
自分で数秒前に行った事を忘れてしまうのです。本当に賢いのか疑いたくなりますが、間違いなく非常に賢いです。それは私も強く感じています・・・
その結果、こちらが指示した作業が無限ループに陥ることが日常茶飯事です。

CursorはエージェントでYoloというモードが指定可能です。このモードでは人間の許可なしに複数ファイルの作成・更新が行えます。
現時点で25回の命令試行が実行可能ですが、「UT実装して、UTを実行して、UTが成功するまで作業お願い」と依頼すると、
**「A→B→C→A→B」**のように循環します。「Aが問題なのでBに修正します。まだ問題が解決しないのでCに修正します。まだ解決しないのでAにしてみます。」といった具合です。

こんな時は

グルグル同じことをして無限ループになっています。
何が問題でどんなアプローチをしたのか、その結果として解決したこと・解決できなかったことを明確にしてください。
まだ試していない観点や実施したアプローチに問題がなかったか振り返りましょう

といったお願いをします。

3. 自分のバグを嬉々として報告するAI

AIは自分が書いたコードに問題があっても、まるで他人事のように 「問題を発見しました」 と嬉しそうに報告してきます。
まあ、前述のYoloモードなのでAIエージェントが勝手に呟いているだけですが・・・

それはあなたが書いたコードのバグなのです。自分で穴を掘っておいて「ここに穴がありますよ」と得意げに教えてくれるようなものです。
心の中では「いや、お前が書いたんだよ・・・」と突っ込まずにはいられません。実際、突っ込みます。

この対策としては、「現時点でnpm run buildで問題は発生していません。npm run lintを実行してwarningと判定された問題を解決して、解決後にnpm run lintを実行して問題が解消していることが確認できるまで作業を継続してください。lintの作業が終了後にnpm run buildを実行して問題あれば対応して、npm run buildが成功するまで作業を継続してください。念押しですが、現時点でnpm run buildは成功しているので、失敗するのは今から行った作業に起因するので、そこんとこよろしく!」
みたいな感じでお願いするのが効果的です。

後日談:現在では、Cursorの「Rules for AI」のショートカットエイリアスを使ってこのストレスは格段に減少しています。

4. 目が見えないAIにとってスクショが重要

AIには目が見えないという制約があります。人間なら画面を見れば一目瞭然のUI不具合はAIが理解できるように説明しないと修正できません。

対策としては、スクリーンショットを生成AIに提示します。
目は見えませんがCursorのエージェントとは画像で会話できます。
ここがこうなっているので、このようにしてほしいとお願いするのですが、なかなかうまくいきません。

例えばボタンの位置がずれているとき、AIにその「ずれ」がなぜ発生しているのかを理解してもらうのは非常に骨が折れる作業です。

生成AIに画面コンポーネントに境界線(ボーダー)を付けたり、背景色でどのコンポーネントがその場所に表示されているかを明確にしてもらうと伝わりやすいです。
まあこの辺りは話が遅いですが・・・

後日談:Claude 3.7 Sonnetになってからe2eの失敗時に作成されるスクリーンショットを自発的に参照してくれるようになりました。再現性がある動作かは未検証です。

5. 自信満々に間違えるAI

生成AIは妙に自信過剰なところがあり、間違えていても堂々としています。特に苦手なのが文字数のカウントです。
それが顕著に現れたのがUTで利用するパスワード期待値でした。

「15文字で」とお願いしたところ、AIは「15文字です」と満面の笑み(のような口調)で返してきました。
ほとんどのモデルで苦手なことが確認済みで、横並びで苦手なのは大人の事情なのでしょうね・・・

自分:「いや、これ10文字なんですけど・・・」

AI:「再度確認しましたが、15文字です」

自分:「いや、これ10文字なんですけど・・・システムコマンドで長さ確認して」

AI:「システムコマンドを実行して確認します」

$ echo "1234567890" | wc -m
10

AI:「申し訳ございません。10文字でした」
こんな感じです。面白くないけど面白い・・・

6. 過去に生きるAI:dateコマンド必須問題

AIは時間がズレている点にも注意が必要です。内部の知識が過去のデータに基づいているため、現在の日付や時間を尋ねても正確に答えられません。
試しに「今日の日付を教えて」と聞いてみたら、AIにとっては知らない"未来"の話らしく、的外れな反応が返ってきました。

そこで登場するのがdateコマンド(現在日時を表示するシステムコマンド)です。
開発中は何度かこのdateを実行してもらって「今は2025年だよ」とAIに気付いてもらう場面がありました。
現代に生きる我々が過去から来た相棒にカレンダーを見せているようなもので、ちょっとしたタイムトラベル気分を味わえます。

これに関連した話題として、利用するライブラリを最新にしていると「このライブラリのこのバージョンは存在しないので、XXXに変更しました」と言われることがあります。

これもdateコマンドを打って納得してもらうか、「.cursorrules」などにバージョンを記載して変更禁止と記載するしかありません。

後日談:最近は少し賢くなって、自主的にシステムコマンドを実行してくれることもありますが、まだまだです。

7. 開発環境の前提を無視するAI

AIエージェントはしばしばシステムの前提(環境)を無視して暴走することもあります。
たとえば「データベースはDockerコンテナ上で動かしているから、ローカルにインストールしなくていい」と念を押して伝えているにもかかわらず、
AIエージェントは「まずはローカル環境にデータベースをインストールしましょう」と指示してきます。Dockerで動かしていると言ったのに

8. 危険なシステムコマンドで環境破壊

AIエージェントは、環境を破壊しかねない危険なコマンドも躊躇なく実行します。

データベースには接続できるけどマイグレーションが実行できないので、コンテナを削除して作成しましょう、などと言ってくることもあります。
ローカルの開発環境であれば許容できますが・・・

ルールに書けば防げますが、ルールのコンテキストも小さくする必要があります。

9. 永遠に終わらない常駐コマンド

Cursorのエージェントは自分の判断で様々なコマンドを実行します。
コマンドには常駐型のものもあり、無限ループや排他処理のミスでデッドロックになることもよくあります。そんなときエージェントはどう対応するでしょうか。

AIエージェント:「正しいコマンドを実行したし、ちゃんと起動してるので待つんだワン!」

こんな感じです。

自分:「またスタックしてる。Cmd + cっと」

AIエージェント:「何らかの理由でコマンドが停止されたので再度実行します」

自分:「・・・」

なんでやねん!と叫ばないためには

生成AI、特にAIエージェントは短期記憶しか持たない優秀なITエンジニアです。
このような特性のエンジニアがどうやって気持ちよく働けるかを考えることが重要です。

コンテキストを適切な大きさに抑える

「.cursorrules」にすべての仕様を書くのは無理があります。
Cursorの「.cursorrules」、「Project Rules」、「Rules for AI」を組み合わせながら、必要なコードと設計書をコンテキストに乗せることを意識しましょう。
情報を入れすぎるとボロボロ忘れてしまい逆効果です。

ログが重要

ITエンジニアにとって最も必要な能力は事象を切り分ける力です。
プログラミングをしているときも、一発で完成形に持っていけるわけではなく、実装とデバッグの繰り返しになります。
実装力も大切ですが、完成形に持っていくためにはデバッグ力=問題を切り分けて解決する能力が大切です。

局所的なコードを書く能力では生成AIは人間をはるかに上回りますが、問題点を切り分ける能力はまだ低いです。
単純にコードを見れば分かる問題なら生成AIの方が優れていますが、複雑な動作をするシステム全体を見て発生する問題は苦手です。
排他制御やOpenGLなどを使ったゲーム開発では絶望感しかありません。
この課題を解決する現時点での方法はログだけです。

ログは多すぎるとデバッグに時間がかかります。また本番環境で大量のログを出力するとインフラコストに影響します。
インフラ担当者はログを抑えるよう求め、開発者はできるだけ多くのログを出して問題解決時間を短縮したいと考えます。
少なすぎても効果がないため、実際の運用を考えたバランスの良いログ出力設計が大事です。

話がずれましたが、生成AIに複雑な事象を正確に認識してもらい修正可能な状態にするにはログがとても役立ちます。
詳細なログを出力してもらって生成AIに読み込ませ、問題点を認識してもらいます。
問題解決後はログは削除する方針で割り切るのが良いでしょう。
ただし、ログが膨大になりすぎて人間がサマリーしないといけない状況は避けるべきです。

追加した詳細ログを取り除くために変更を取り消し、必要なものまで消してしまった経験は最悪でした。ご注意ください。

小刻みにバージョン管理にコミットする

Cursor単体でも変更履歴を保持していますが、小さな目標を達成するたびに小刻みにバージョン管理システムにコミットすることをお勧めします。

生成AIが混乱した場合は変更内容を取り消し、取り消した旨をエージェントに伝えます。
現状のファイル内容を確認し、何を達成しようとしていたか、何が実現できて何が実現できなかったかを整理して、一から作業してもらいましょう。
エージェントだけでは解決しない場合は新たな観点を追加するのが良いです。
また、参考になるURLを指定するのも効果的です。

作業内容が変わる場合はNew Chat

作業の切り替えタイミングで新しいComposerに切り替えると不要なコンテキストが消えます。
Cursor 0.46からUIが大幅に変わったので、現在は「New Chat」です。

参考:Cursor 0.46からはNew Chat

選定した技術スタックに対するふりかえり

今回は「Full-Stack TypeScript」ではなく、バックエンドにKotlinを採用し、バックエンドとフロントエンドを明確に分離し、ドメイン駆動設計を適用しました。

分離されたドメイン境界やエンティティ、値オブジェクト、ドメインイベントなどによって、関心の分離を適切に行い生成AIのコンテキストを小さく保てます。
ドメイン駆動設計とAI駆動開発の相性が良いという結論に至ったことは、今後の開発アプローチに大きな示唆を与えていると感じます。

結論:現状では失敗の山を越えられる者だけがAI駆動開発を実践できる

最後に、今回の経験から得た結論を述べます。
確かにAI駆動開発は小規模で単純なアプリなら驚くほどうまくいき、生産性向上の夢を見せてくれます。

しかし拡張性や保守性を求める本格的な開発になると失敗の山を築くことになります。
近くに同じように苦労した人がいてアドバイスをもらえるなら小さな山で済むかもしれません。

そんな山を築き、乗り越えられるだけの経験とタフさ、そしてAIエージェントが生産性を何倍にも引き上げてくれるという確信を持つ者だけが、現時点ではAI駆動開発を実践できると思います。

たいそうな書き方になりましたが、それでもその困難に挑戦する価値はあります。
苦労の連続でしたが、AIエージェントと共にプロダクトをリリースする体験は新鮮で刺激的でした。
1年後には状況が大きく変わっているでしょう。
AIがさらに賢くなり、今回紹介したような珍事件が笑い話として語られる日が来るでしょう。
それよりも、お仕事が無くならないか心配ですが・・・
ということで、今日も私は失敗の山を築きながらAIエージェントと格闘中です

あ、モノレポの話を忘れていました。より深掘りした内容(本当に深いかは・・・)を次回以降で記載します。

用語説明

  • JetBrains: IntelliJ IDEAやWebStormなど、様々なプログラミング言語に対応した高機能IDEを提供する企業。All Products Packは同社の複数IDEをまとめて利用できるサブスクリプションプラン。

  • GitHub Copilot: OpenAIの技術をベースとしたGitHub公式のAIペアプログラミングツール。コード補完や提案を行う。

  • Cursor: ChatGPT的なAIモデルを統合した開発環境(IDE)。エディタと会話型AIが組み合わさっており、コードの生成や修正を「エージェント」との対話で行える。

  • Composer: Cursorの機能・モジュールの1つ。テキストベースのやり取りやチャット画面でAIに指示を送る仕組み。

  • Yoloモード: Cursorのエージェントが、人間の許可無しでファイル更新を行うモード。自動で試行錯誤するが、無限ループに陥りやすい場合がある。

  • .cursorrules: Cursorが実行するコマンドや動作を制御するための設定ファイル。

  • New Chat: CursorのComposerで新しいチャットセッションを開始すること。以前のコンテキストをリセットする。

  • ピープルウエア:1987年に初版が出版されたトム・デマルコとティモシー・リスターによるソフトウェア開発における人的要素に焦点を当てた名著。単なる技術書ではなく、ソフトウェア開発チームのマネジメントと生産性向上に関する人間中心の視点を提供している。第3版まで改訂され、ソフトウェア開発の古典として現在も多くのエンジニアやマネージャーに読まれている。ピープルウエア 第3版

  • ダース・ベイダー型開発者:ピープルウエアで言及される「ダース・ベイダー」とは、品質と技術的な完璧さに対して一切の妥協を許さないタイプの開発者を指す隠喩。スター・ウォーズの悪役にちなんだ表現だが、ここでは「悪役」という意味ではなく、冷徹なまでに高い基準を持ち、技術的な卓越性に対して情熱を持つ職人気質のエンジニアを表している。チームの平均的な判断や経営判断よりも技術的な正しさを優先するため、組織の中では「扱いにくい人材」と見なされることもあるが、その技術力と妥協のない姿勢は製品の品質向上に大きく貢献する場合もある。
    このような「ダース・ベイダー型」の開発者は、特に旧来の大企業の評価システムでは、協調性や組織への順応性が重視される傾向があるため、その真価が正当に評価されにくいという側面があります。

Discussion

ominiomini

なかなかこういった記事はないので面白かったです!

KAWANO FumihikoKAWANO Fumihiko

コメントありがとうございます!
面白くしようとし過ぎて、なんか無駄な苦労してる人の話って思ってる人が多い気がします。
ちゃんと読んでいただければ、そうじゃないって認識いただけると思うのですが・・・

ominiomini

無駄な苦労ではないですよ(笑)!!
今はまだ「AIってすごい!便利!」的な記事があふれているので、現場に導入した際の現実的なむずかしさがみんなよくわかってないと思います。そういった意味でも実際の経験をシェアしていただけるのはとても意義のあることです🎊
続編お待ちしております!!

MabbyMabby

現実に則した内容、感謝です。
こういった投稿が増えてくればSNS上のヒステリックなポストも減っていくでしょうね。。。

KAWANO FumihikoKAWANO Fumihiko

現実に即しすぎて、やっぱりまだAI駆動開発は苦労が多く、自分の手でコード書いた方が良い!
って勘違いされる方が多い気がします。実際やってみるとほんと苦労しかないけど、ノウハウためれば現状でも3倍ぐらいの生産性は余裕で出るのですが・・・、コメントが無限ループになりそう・・・

MabbyMabby

念の為補足しますが、猛烈なスピードで技術がアップデートされる中、エンジニアのみなさんはそれぞれ立場や働き方が多様なわけで、生産性への寄与については現状では個々で判断するしかないのだろう、と。ただ、現状では現実的な判断材料がとても少ないわけで、アーリーアダプターの現実的なフィードバックは貴重です。Silent Majorityは近い将来AI活用が前提となるだろうことは認識しているでしょうから、彼らがAIとどう付き合っていくか、どのタイミングで切り替えていくか、という判断するための良い材料になります。

PolishYouPolishYou

何度かCursorは使ってみたけど適切なプロンプトを与えるのが大変すぎてストレスですね
プログラミングと違って指示出しは全く面白くないし…
提案されたコードを受け入れたあとで問題が発生したら他人(AI)が書いたコードをデバッグする必要も生じる
現状は向いている人、向かない人に分かれると思います。根気強い人じゃないと厳しい
将来的にそこまで工夫しなくともある程度の水準のコードを書いてくれるようにはなるかもですね

KAWANO FumihikoKAWANO Fumihiko

コメントありがとうございます!
生成AIエージェントはかなり優秀でメッチャ手が早い癖の強いITエンジニアです。
AIエージェントが書いたコードでも、同僚でも、協力会社の方でも、自分がレビューして品質を担保して、お客様に品質の担保されたものを提供する!、ってのは20年以上前から変わりません。
利用者してくれる方に価値あるものを提供し喜んでいただくことがITエンジニアの本質です。コードを誰が書いたかって重要ではないです。