🏗️

LLMのコーディングエージェント(主にCodex)を効率よく使うために

に公開

前提

私が現在使っているLLMコーディングエージェントは下記です。

  • Codex(proプラン $200)
  • Cursor(proプラン $20)
  • Gemini(workspace Business)

1年前にclineを使いはじめ、今年の春ごろにmaxプラン($100)でClaude Codeが使えるようになったタイミングでClaude Codeに乗り換えました。
その後、gpt-5-codexが登場し、maxプランの残期間があったClaude Codeと併用していましたが、同じ不具合でもCodexはすんなり不具合を解決できるのに、Claude Codeだといつまでたっても解決しないということが何件かあり、またクラウドでも依頼できるという点に魅力を感じて、値段があがるもののCodexに乗り換えました。

HubspotというCRMを使ったRailsアプリを、TwentyというオープンソースのCRMにも対応させる際、
「生成AIを活用すれば1週間もかからないだろう(開発者は私1人)」と思っていましたが、実際には3週間かかりました。
そのときに得た知見をまとめます。


1. コーディングエージェントを使い分ける

思っている以上に、AIモデルによって性質が異なります。

Codex(gpt-5-codex)

実装力が強く、テストが通るまでしっかりと実装してくれます。
デバッグにも強いです。
ただし大掛かりな修正を依頼すると、すぐに「BigJobだ」と言って手を抜く傾向があります。
リファクタリングをお願いすると、コード量が逆に増えることのほうが多いです。

たとえば、CRMアクセス用のサービスクラスがあり、CRMのAPIを使って直接アクセスしている部分だけをドライバークラスとして切り出し、あとでSimulatorと切り替えやすくしてほしいと依頼したところ、「BigJobだ」と言って元クラスを継承したクラスでお茶を濁しました。
「継承はやめてください」と言うと、モジュールにしてextendし、結局継承と同じことをし始めます。

実装から仕様ドキュメントを書かせると、しっかりとした内容を出してくれます。
ただし、「各メソッドにコメントを付けて」とお願いすると「2000行以上のファイルですが本当にやりますか?」と聞いてきたり、実装の中身ではなく単なるメソッド名の翻訳だけを出したりと手を抜くこともあります。


Gemini(Gemini 2.5 pro)

とても素直です。
gpt-5-codexが拒否したCRMサービスクラスの外部アクセス部分をドライバークラスとして切り出す依頼にも、すぐに「わかりました」と対応し、こちらの意図どおりの雰囲気に仕上げてくれます。
ただし、テストが通りません。syntaxエラーもありました。テスト方法を教えても解決力が弱いのでいっこうにテストが通るようにならないです。

簡潔なドキュメントを作るのが得意で、メソッド定義のコメントを実装を見ながら書いてもらうと、RDocなどで自動生成できるフォーマットでいい感じにまとめてくれます。
ただし、実装から現在の実装ドキュメントをまとめさせると簡潔すぎて情報が欠落することがあります。


Claude Code(Claude Sonnet 4)

フロントエンドを見栄えよく「よしなに」やってくれます。
ただし実装力が弱く、デバッグにも強くないため、現在は使っていません。
4.5は未検証です。


結論

仕様書をもとにテスト込みでCodexに実装を依頼します。
ただし、新規ではなく、改変の場合等は質があまりよくないことが多いです(テストを通すために、idだけでなくorderIdなど呼び出し元を統一せずにメソッド側で複数のパターンの引数を取ったり、各ファイルで似たようなメソッドを定義したりするなど)。
そのためGeminiにリファクタリングを依頼します。

Geminiは雰囲気で書いてくるのでテストが通らなくなりがちですが、再度Codexに「テストが通るように」と依頼すれば修正してくれます。

各ソースファイルのメソッド定義コメントはGeminiに依頼します。
Geminiは2000行ずつしか読まないので、なるべくソースコードは2000行以内にするようにしています。
無料枠でも日々ちょっとずつメソッド定義を書きすすめていってもらうのがおすすめです。

現在の実装をもとに、再度実装に関する詳しいドキュメントをCodexに作成してもらいます。
そのドキュメントを次回からのプロンプトに付与することで効率的に依頼をすすめてもらえるようになります。


2. おおまかな戦略は自分で考える

プロダクトの状況や設計意図は生成AIが考慮しないため、開発者自身が先を見通してきっちり指示する必要があります。

今回の例である実装済みのHubspot CRMを使ったアプリをTwenty CRMにも対応させるやり方はいくつかあります。

  1. アプリのサービスやコントローラで、現在使用しているCRMがHubspotかTwentyかを判定し、それぞれのモデルを呼び分ける。
  2. アプリのサービスやコントローラは既存のままで、モデル内でHubspotServiceとTwentyServiceを切り替える。
  3. CrmServiceというサービスを作成し、モデル側でHubspotServiceを呼びだしている箇所をCrmServiceに書き換えるだけで、CRMの違いを意識せず利用できるようにする。

1が最も単純で、既存の実装をほとんど触らずにTwenty対応を追加できます。
ただし処理が重複しやすく、同期漏れや実装の混乱が起こりがちなので避けたい方法です。

2はやや改善されますが、本質的には同様の問題を抱えます。
CRMの機能差が大きい場合には、あえてこの方法を選ぶこともありえます。
3は、一度作れば今後の拡張が容易で、両方のCRMに対応できます(例えるなら、DBアダプタがsqlite3かPostgreSQLかをアプリ側は普段意識しないで実装できるイメージ)。
ただし既存実装に手を入れる必要があり、パラメータの変換や呼び出し方式の差異を吸収する工夫も求められます。
Hubspotはsnake_caseでidが数値型、TwentyはcamelCaseでidが文字列型(UUID)などの違いもあり、enum制約(Twentyは256バイトまででcamelCaseの英数字のみ)も異なるため複雑です。

Codexなどに依頼すると、ほとんどの場合1か2を選択します。
私も最初は2で進めましたが(1.ではなく2.にするためにサービスで一部直接HubspotServiceを呼んでいたところをモデル経由に変更した)、コードが複雑化し、テストの実装も実連携を使ったりStubを使ったりの実装のため、重いしテストコードが読みづらくなりました。
結局そのため3に移行しましたが、2→3への移行だけで10日間かかる結果となりました。。

また、2→3への移行は生成AIに頼んでも思ったように進まず(Codexは全然実装しようとしない、Geminiもファイル分割しだしたり余計なこともしはじめて、余計な混乱を産みだしてアテにならず。。)、結局途中まで自分でCrmServiceを実装して、テストがちょこちょこNGがあるだけの状態にしてからCodexに続きを任せました。
CRmServiceを自分で実装するにあたって、 Codexには、Twentyのレポジトリからソースコードをダウンロードして、そのコードをもとに生成AIにAPI仕様書を作成してとお願いて作成したAPI仕様書を渡していましたが、それだけでは私にはよくわからなかったので、さらにTwentyのソースコードからカスタムフィールドやカスタムオブジェクトについての仕様やRelationのの仕様や仕組みをそれぞれドキュメントにしてもらって読みこむことで私自身もTwentyに対しての知識を深めることになりました。
元からあるAPI仕様書にそれらの情報を追記することで、生成AIにとっても仕組みを理解しやすくなり、生成するコードの生成がはやくなりました。

既存コードにも大幅に変更が必要だったため(共通化するにあたってモデル側でHubspotの低レイヤーな実装をしている箇所を切りだしてモデル側ではなくCrmService側でその実装を行うようにする)、既存実装で問題なく動作しているHubspot側にも自動テストが必要な状況になりましたが、Hubspotの実サービスにアクセスするテストはレートリミットや作られたレコードが他へ影響することを考えるとできず、mockだと連携した試験がうまくできないし、テスト確認内容も大丈夫かどうかも怪しいという状況を改善するために、外部API呼び出し部分を抽象化して、テストの場合はlocalで動くSimulatorに切り替えるといった対応も必要になりました。

3まで進めるとようやくソースの見通しがよくなり、各コードが何をしているかが明確に分かるようになりました。
2の状態ではコードを読んでも意図が分からず、「これがバイブコーディングか」と痛感しました。

もし時間の制約で2の状態のままリリースしていたらと思うと恐ろしいです。
最初から3で進める方針を決め、CLAUDE.mdなどにその方針を明記しておけば、5日間は短縮できたと思います。


3. 依頼するタスクを細かく分割する

Codexは「15分一本勝負」のような性格があるように感じます。
大きなタスクを渡すと、未実装部分が多い中途半端な成果を出してきます。

別プロジェクトでコード移植を依頼した際も、「元コードがあるから楽だろう」と単に「移植して」と依頼したところ、未実装だらけでした。
そこでまず移植対象メソッドの一覧を出してもらい、その後、メソッド単位でテストを書きながら実装を依頼したところ、しっかり移植してくれました。


4. ロジックで解決することも、いったんデータとして掃き出して使う

たとえばHubspotとTwentyでは、フィールド名をsnake_case ⇔ camelCaseに変換する必要があり、さらに例外的なケースは設定ファイルに変換元フィールドと先のフィールドを明示的に定義していてその場合はそちらを使うよう変換するメソッドを作っていました。
TwentyのAPIを呼び出す前にその変換メソッドを呼ぶことでフィールドの変換に対応できますが、更新処理が必要な機能の実装を生成AIに依頼すると、生成AIは更新APIについてだけでなくその変換ロジックについても理解し、正しいかどうかの検証もはじめだすためコンテキストが大きくなり処理も重くなります。

そのため、予めロジックに基づいて使用するすべてのHubspotとTwentyのフィールド対応表をYAMLで書き出すツールを作り、マスターデータとして扱うようにしました。
また、そのマスターデータに基づいたカスタムフィールドの反映や正しく反映されたかのチェックを行うツールを作成しました。
それらを実行することで生成AIにとってのアプリで起こった更新エラーの問題の切り分けをフィールドの変換ロジックのミスや反映もれがあるかもという考慮が必要がないと伝えた状態にすることで、不具合の問題解決のスピードや生成されるコードの複雑さがなくなりました。

最初のうちはサーバーからエラーが返ってきた場合は、反映もれとして新規でフィールドを作るロジックが実装されていたり、metafieldをすべて取得して、近いフィールドを使いだすロジックがはいっていたりとごちゃごちゃしていて、ひとつすっきりさせると他でエラーがおき何がどうからんでいるのかわからない状況でした。。


5. ドキュメントを頻繁に更新させる

コードだけでは理解が難しい部分も、文章で補足があると把握しやすくなります。
また、実装方針はコードからは分かりません。

そのため、依頼した実装が完了したタイミングで「ここまでの知見を各mdファイルに追記してください」と依頼しています。
たとえば:

  • テストに関する知見用のmd
  • Hubspot→Twenty移行ノウハウ用のmd
  • Twenty関連仕様のmd

関連するmdをプロンプトに渡し忘れると、AIが無関係なファイルを大量に読み込んだり、意図と異なる実装をしてしまうので、これらのファイルを依頼時に指定および更新することは大事です。


6. 残り2〜30%になった時点で引き継ぎプロンプトを書いてもらう

処理途中でコンテキストが少なくなると、精度が落ちることが多いです。
またcompactを使うとcontextが失われるます(今までのやりとりを要約した状態になるとありますが、経験的にはほぼ空になってる気がします)

そこで「引き継ぎ用プロンプトを書いてください」とお願いすると、これまでの対応内容・調査内容・現状の課題などをまとめてくれます。その内容をcompact後の新しいチャットに渡し、必要なmdも併せて提示すると、以外とすんなり解決することが多いです。


7. Codex(エージェント版)用のテスト環境を作る

普段はCodex CLIを使っていますが、寝ている間や移動時間中にWebのCodex(エージェント版)へ依頼することがあります。

クラウド版は独自コンテナ上で動作するため、外部接続がほとんどできず、bundlerでライブラリを取得しようとしても失敗します。
その結果、実装してもらってもテスト(rails test)が実行できず、結果中途半端だったりバグのある実装ができあがります

解決策として、必要なgemをあらかじめgitに含めておきます。
手順がややこしいので、以前Zennにまとめました。

👉 Codex環境クラウドでbin/rails testを実行できる環境にする手順


8. 依頼した実装が終わるごとにcommitをする

うまくいかなくなった時にCodexがコードをcheckoutで戻すことがあります。その際に前回の修正箇所をaddやcommitしていないと前回の依頼の結果まで含めて指し戻ってしまいます。resetされるパターンもあったのでcommitのほうが確実です。不要なcommitだとしてもresetすればいいだけですし、resetした後やっぱり欲しいとなった時もreflogを辿って戻すこともできますし。
diffをすれば現在依頼に対してどのような変更がされたのかもすぐわかります。
あとでrebaseすればきれいにできるので、たとえtestが通らない状態になっていたとしてもとりあえずcommitしてしまうのがいいです。


9. E2Eに近いテストを書く(書いてもらう)

当たり前ですが、とても重要です。
生成AIを使えば問題がないコードが出るってことはなく、むしろ全体コードを認識していないので、修正が他の範囲に気づかずに影響でることがしょっちゅうあります。
Stubを使わずE2Eに近いレベルのテストが充実していることが理想です。
そのためには外部APIの呼び出しがTestではできない場合は、外部APIの変わりに動くSimulatorを作るなどが必要です。
生成AIが生成したStubはただ単にメソッドが呼ばれているかのテストなどもあったり意図がよくわからないことが多く、変更にも脆弱です。リファクタリングでエラーが出るようになると、Stubだと不具合かどうかのエラー調査やテストの修正がとても大変です。

テストがしっかりしていれば、Codexは本当に心強い存在になります。


10. 仕様書をちゃんと書く

一番重要ですが最後なのは、最初に書くと当たり前すぎて引かれると思ったからです。

仕様書は最重要資料です。
何をしたいのか、その意図を正確に伝えるために欠かせません。
生成AIだけでなく、自分自身やユーザーに対しても、どういう機能なのかを明確にする必要があります。

仕様書に書かれている内容をもとに「足りないテストを追加して」と依頼したところ、テストによって不具合を発見したこともあります。
仕様書がないと、せっかくAIにドキュメントを書いてもらってもAIが変数名から意味不明な単語を使ったドキュメントになってしまいます。


まとめ

AIにより自分の能力をブーストできるので、今までは工数がかかりそうでためらっていた機能にとりかかれるようになりました。
Twenty CRM対応も生成AI抜きではそもそもやらなかったはずです。
今までは生成AIを使ったコーディングでつまづくことなく生産性があがっていたため(自分が外部APIに対しても理解した上で依頼していた)、今回のTwentyというドキュメントがほとんどなく、CRMサービスとして使ったことさえないけど、私の知識がなくてもオープンソースだしなんとかなるかと生成AIを過信しすぎていました。
結果、予定時間がすぎても全然実装が終わらないので休日もずっと使うことに。。(依頼から結果に時間がかかるので休日でもそこまで苦にはならなかったですが。)。。

生成AIを使ったコーディングで開発するおおまかな流れを下記に書いてみたら、流れは人が作るのと同じでした^^

  • やりたいことをはっきりさせ仕様書を作る
  • 実装方針を決める
  • やるべきことを細かくタスクに分割する
  • タスクに対して実装する、テストを作る、リファクタリングする、実装内容をドキュメントに残す。

Codexに依頼するポイントは、依頼内容をいかにコンパクトに明確な指示を与えるかだと思います。
実装方針を決めるのはスキルや経験が必要ですし、タスクに分割したりリファクタリング依頼をするためには実装の理解も必要なので、結局自分ができないことはAIにもできないことが今回の経験でよくわかりました。

Discussion