私がLLMと一緒にコーディングする方法

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

最近は多くの人がそうであるように、私もLLMと一緒にコーディングすることが増えてきました。私がLLMと一緒にコーディングしていると言うと、意外だという反応をする人もいれば、どのようにLLMを活用しているのかという質問をよく受けます。そこで思い立ったついでに、私がLLMをコーディングにどのように活用しているかを大まかに書いてみようと思います。

前提

当然ながら、私のLLM活用方法は私が主に扱う種類の作業に合わせたものです。したがって、一般的な他のコーディングには適していない場合もあるでしょう。私が主に扱う作業とは、協力者や消費者と主に文書を通じて非同期でコミュニケーションを取るオープンソースプロジェクトであり、それもアプリケーション開発ではなくライブラリ開発が中心です。主に使用するプログラミング言語はTypeScriptで、LLMが比較的得意とする部類に入ります。一方で、既存の知識が比較的速く陳腐化していくエコシステムでもあるため、ある面では不利な点もあると言えるでしょう。

いずれにせよ、私のLLM活用方法は私が主に扱う種類の作業に合わせたものですが、それでもこの記事では一般的なコーディングに広く適用できるヒントを共有するよう努めました。

コンテキストが王様だ

LLM活用において、モデル自体の性能など様々な考慮事項がありますが、人々がLLMを使用するのを観察したとき、最も見落としがちなのが十分なコンテキストの提供です。人々は自分が何かを判断するとき、どれだけ多くのコンテキストに依存しているかを意識していません。頭の中の些細な記憶の断片から、私がアクセスできる最新のドキュメント、イシューに含まれたキャプチャー画像まで...これらのほとんどは、LLMが基本的には単独でアクセスできないことが多いのです。どれだけLLMが賢くても、必要なコンテキストが与えられなければ、的外れな結果を出してしまうものです。周りで「LLMはコーディングが下手すぎる」と嘆く場合を見ると、本当にLLMが解決困難な問題を主に扱っている方々もいましたが、ほとんどの場合はLLMに十分なコンテキストを提供できていないことが原因であることが多かったです。

おそらくこの記事で扱うほとんどの話は、結局「どうやってLLMにコンテキストをうまく提供するか?」という悩みから生まれたヒントだと見ても差し支えないでしょう。

私が使うモデルとコーディングエージェント

2025年9月現在、私はほぼすべての作業にClaude Codeを使用しています。ここ数ヶ月はClaude Codeを主に使ってきており、定期的に他のツールも試していますが、依然としてClaude Codeが私に最も合っていると判断しました。実は、私はほとんどのプログラマーにとってClaude Codeが最も適していると思っています。厳密なベンチマークに基づいたものではなく、体感に過ぎませんが...理由は次の通りです:

  • 他のモデルはツール呼び出しが苦手です。ツールを提供すれば、必要な瞬間にツールを活用できるべきです。Claudeモデルはこの点で確かに優れています。ツール呼び出しは豊かなコンテキストを提供するために必須であるため、ツール呼び出しのパフォーマンスが低下すると、結果的にLLMを伴うコーディング自体の成果が下がります。

  • 他のモデルは質疑応答が長くなると性能が低下します。少なくとも私はLLMとコーディングするとき、一発(one-shot)で結果を出す方式、つまりバイブコーディング(vibe coding)を好みません。したがって、モデルのマルチターン(multi-turn)性能が重要ですが、他のモデルは質疑応答が長くなるにつれて、前に話したことを忘れる傾向があります。

  • 同じClaudeのモデルを使用しても、Claude Code自体の性能が他のLLMコーディングエージェントに比べて優れています。これはファインチューニングやシステムプロンプトなどに秘訣があるからだと考えられます。このため、Claude CodeをClaude以外のモデルと一緒に使えるようにする非公式プロキシなども存在します。

もちろん、ClaudeおよびClaude Codeにも欠点があります:

  • 比較的コンテキストウィンドウ(context window)が短いです。そのためトークンを節約する必要があります。Claude Codeの会話圧縮(conversation compaction)はそれなりにうまく機能しているとはいえ、依然としてストレスではあります。(例えば、会話圧縮は英語で行われるため、後続セッションでは突然英語で回答し始めます。ああ、私はプロンプトを韓国語で書いています。)

  • 一部のLLMコーディングエージェントが提供するLSPサポートがまだありません。したがって、タイプエラーやリントエラーなどをコマンド実行などを通じて別途表示できる必要があります。代わりにClaude Codeではフック(hooks)機能が提供されるため、これをうまく活用すればある程度は似たような効果を得ることができます。

しかし長所が短所を上回るため、今後も状況に大きな変化がなければClaude Codeを主に使うことになりそうです。

詳細な指示は書面で

プロンプトは詳細であるほど良いです。プロンプトが一文で終わるなら、良いプロンプトではない可能性が高いです。時にはプロンプトを作るためのプロンプトが必要な場合もあります。私のプロンプト方式は次のとおりです。

まず、ほとんどのプロンプトはGitHubイシューに作成します。必要な十分なリンクを提供する必要があり、可能な限りキャプチャー画像や図表などの視覚的情報に大きく依存しないようにする必要があります。もちろん、イシューは基本的にLLMのためのものではなく人のためのものなので、LLMだけに必要な情報はイシューに含めたくない場合もあるでしょう。そういったものはイシューに含めなくても構いません[1]。すでに他の人が作成したイシューがあれば、そのイシューを活用しても良いです。他の人が作成したイシューにコンテキストが十分でないと思われる場合は、コメントでコンテキストを補足します。

時にはイシュー自体もLLMで作成することもあります。関連文書や状況を十分に共有してイシューを作成してもらうという方法です。例えば、次は私が作成したメール送信ライブラリであるUpyoPlunkトランスポートを追加するイシュー #11 Plunk transportを作成するために使用したプロンプトです:

Plunkというメール送信プロバイダーがあります。PlunkのトランスポートをUpyoに追加すると良いと思うのですが、イシュートラッカーにまずイシューを作成したいと思います。イシューのタイトルと内容を英語で作成していただけますか?問題定義と解決策提示が区分されるような形式的な文章ではなく、もう少し人が書いたような自然なトーンでお願いします。長すぎる必要もなく、1〜2段落程度で十分だと思います。

参考リンク:

ただし、この時私はClaudeのプロジェクト機能を利用して、Upyoの既存ドキュメントをRAGで提供した状態で指示を出したことを明記しておきます。

次にClaude Codeのプランモード(plan mode)[2]で次のように指示します。

https://github.com/dahlia/upyo/issues/11 イシューを実装する必要があります。イシュー本文と本文でリンクされている関連リンクをすべて確認した後、UpyoプロジェクトにPlunkトランスポートを追加する実装計画を詳細に立ててください。

正確には、私はイシューリンクを提供する代わりにイシュー番号だけを提供し、GitHub MCPを使ってイシューを直接読み取るようにすることを好みます。HTMLではなくMarkdown形式でイシューを読むため、本文に貼られたリンクなどをより適切に追跡できるからです。リンクが本当に重要な場合は、Claude Codeの指示でもう一度記載することもあります。イシューに書ききれなかったLLMだけのための情報もこの時にすべて記載します。

実装時に確認すべきソースファイルが何かをよく知っている場合は、そのような情報も一緒に提供するとさらに良いです。LLMがコードベースを探索するための試行錯誤が大幅に減り、トークンも節約できるからです。

私はGitHubイシューを詳細な指示を書く用途で使いましたが、PLAN.mdのような文書ファイルを作成してそこに書く方法も広く使われていると認識しています。

設計は人が、実装はLLMが

私がLLMをコーディングに活用する際の基本的な原則は、大きな設計は自分自身で行い、詳細な実装はLLMに任せるということです。指示する際は設計意図を正確に提示し、実装過程で間違える可能性がある懸念点について十分に言及しておきます。特に、私はプロジェクトを最初に始める時、ディレクトリやパッケージ構造を依然として直接手作業で行うことが多いです。(ただし、この部分は私がもともとLLM時代以前からクッキーカッター系のプロジェクトテンプレートも好まなかったからかもしれません。テンプレートを使おうとLLMを使おうと、私の気に入るように出来上がらないため。)

LLMは自分が最も馴染みのある技術で問題を解決しようとする傾向があるため、技術選択においても明示的に指示することが良いでしょう。まだ些細なライブラリ一つでさえ人間がレビューする必要があります。LLMにすべてを任せていると、セキュリティパッチもされていない古いバージョンを使ってしまうことが多いからです。私の場合は、後述するAGENTS.md文書で、ライブラリをインストールする前にnpm viewコマンドを通じて該当パッケージの最新バージョンが何かを先に確認するよう指示を含めることもあります。

抽象化を行う際も、少なくともAPI設計は依然として私が直接行うことが多いです。なぜでしょうか、LLMが設計したAPIはまだあまり良くないことが多いです。私が受けた印象では、LLMは実装していく中で必要な時にAPIをアドホック(ad-hoc)に設計することが多いということです。もちろん、人間でもこのような方法を好む場合があり、そのような場合はLLMが設計したAPIに不満がないかもしれません。しかし少なくとも私の場合は不満足なことが多いです。

結局のところ、LLMを万能の奴隷ではなく、賢い部分もあるが未熟な点も多い同僚として見て、LLMが不得手な部分には最大限人間が助けて仕事を成し遂げるという視点が必要なようです。

AGENTS.md

ほとんどのLLMコーディングエージェントはAGENTS.mdまたはそれに準ずる機能を提供しています。例えばClaude CodeはCLAUDE.mdファイルを参照し、Gemini CLIGEMINI.mdを参照するといった具合ですが、徐々にAGENTS.mdファイルへと標準化されつつあります。私は特定のベンダーだけで使用されるファイルをすべてAGENTS.mdへのシンボリックリンクにした上で、AGENTS.mdファイルのみを正本としています。こうすることで、私と異なるLLMコーディングエージェントを使用する協力者たちと同じガイドラインを共有できます。

いずれにせよ、AGENTS.md文書の役割は簡単です。このプロジェクトに関するガイドライン、つまりシステムプロンプトです。ほとんどのLLMコーディングエージェントはこのファイルを自動的に生成する機能を提供しており、使わない理由はありません。自動的に生成させた後、間違っている部分だけ修正して使っても構いません。それよりも重要なのは、時間が経つにつれてAGENTS.md文書が古くなることを避けることです。AGENTS.md文書は継続的に磨き上げる必要があります。特に、大規模なリファクタリングがあった後などには必ずAGENTS.md文書を更新する必要があります。この指示自体もAGENTS.md文書に入れておくと良いでしょう。

ではAGENTS.md文書にはどのような内容を入れるべきでしょうか?私の場合は次のような内容を入れています:

  • プロジェクトの目標と概要。リポジトリURLを記載しておくことも意外とGitHub MCPなどを活用する際に役立ちます。
  • プロジェクトが使用する開発ツール。例えばnpmは絶対に使わずpnpmだけを使うといった指示を含めます。
  • ディレクトリ構造と各ディレクトリの役割。
  • ビルドおよびテスト方法。特に、そのプロジェクト特有の特殊な手順があれば必ず記述します。例えば、ビルドやテスト前に必ずコード生成を行う必要がある場合、これについて書く必要があります—もちろん、最も良いのはビルドスクリプトでそのような手順を自動化することです。その方が人間にも良く、トークンを節約するのにも良いです。
  • コーディングスタイルやドキュメントスタイル。フォーマッターの使い方を記載するのも良いでしょう。
  • 開発方法論。例えばバグを修正する際には回帰テストを先に作成し、テストが失敗することでバグが再現されることを確認してからバグ修正を行うというガイドラインなど。

まずは上記のように始めて、LLMコーディングエージェントを活用しながら目につくミスをLLMがするたびにガイドラインを追加することをお勧めします。例えば、私はTypeScriptプロジェクトでanyタイプやasキーワードを避けるようにというガイドラインを追加する傾向があります。

以下は私が管理しているプロジェクトのAGENTS.md文書です:

文書提供

LLMに知識カットオフがあることはよく知られています。つい最近登場したモデルでない限り、私が使用するライブラリやランタイムなどのAPI、CLIツールなどについてやや古い知識を持っている可能性が高いということです。さらに、もしマイナーなプログラミング言語やフレームワークなどを使用している場合、この問題はさらに大きくなります。

したがって、私が使用するプログラミング言語やフレームワークなどに関する知識を提供する必要がありますが、最も簡単で効率的な方法はContext7をMCPとして接続することです。Context7は様々な技術文書を比較的最新版に保ちながらベクトルデータベースにインデックス化し、LLMが要求した場合に関連する文書の断片を提供するRAGサービスです。新しい文書を追加することも容易で、もし私が必要とする文書が登録されていなければ、いくらでも新たに追加して使用することもできます。ただし、特に指示しない限りContext7を別途活用しない場合もあるため、「Context7 MCPを通じて関連文書を確認してください」といった指示が必要な場合があります。

RFCなどの技術仕様書を提供する際は、プレーンテキスト形式が提供されるため、プレーンテキスト文書のリンクを提供するのが良いでしょう。私の場合、連合宇宙(fediverse)関連の開発を多く行うためFEP文書を提供する必要が多いのですが、この場合もHTMLでレンダリングされたウェブページではなく、Markdownソースファイルのリンクを直接提供するという形で使用しています。

このほかにもウェブサイトで/llms.txtおよび*/llms-full.txtファイルを提供する慣行が広がっているため、これを活用するのも良いでしょう。(私が作成したソフトウェアライブラリの場合、プロジェクトウェブサイトですべて/llms.txtおよび/llms-full.txt*ファイルを提供しています。)

ただし、どれだけLLMに親和的なプレーンテキストであっても、技術文書全体をすべて提供するのはやはりトークンの無駄が激しいため、Context7を使用できるならContext7を使用することをお勧めします。

プランモードの活用

Claude Codeを含め、最近の多くのLLMコーディングエージェントはプランモードを提供しています。いきなり実装するのを防ぎ、実装計画をLLM自身に立てさせた上で人間が先にレビューできるようにするものです。特に、私はプランモードでは高価なClaude Opus 4.1を使用し、実際の実装では比較的安価なClaude Sonnet 4を使用する「Opus Plan Mode」を使用しています[3]

私は計画を綿密に検討し、少しでも納得がいかなければいくらでも計画修正を要求します。作業によって異なりますが、どんな作業でも少なくとも3〜4回以上は修正を要求しているようです。逆に言えば、この程度まで計画を練り上げなければ、私が望む方向で実装しない可能性が高いということです。LLMは私とは異なる前提を持つことが多く、様々な詳細計画で私とは同床異夢をしている可能性が高いです。そういったものを事前に最大限取り除いて、私の意図に一致させる必要があります。

自ら学習フィードバックループを回すように

LLMコーディングエージェントで開発を行う際に最も重要だと考えられる部分は、自らある程度方向を調整できるよう環境を整えることです。LLMの様々な実装ミスを一つ一つ私が指摘するのではなく、各種自動化されたテストと静的解析を通じて自ら気づいて実装を修正できるようにすることです。

例えば、CSSバグを修正する場合を考えてみましょう。LLMにCSSコードを修正させた後、私がウェブブラウザを確認する方式は非常に面倒です。代わりに、Playwright MCPを接続して自ら画面を見ることができるようにする方が良いでしょう。要するに、LLMの作業結果が要件を満たしているかを自ら判断できるようにして、要件が満たされるまで作業を続けさせることです。

同様の理由から、実装に先立ってテストコードを先に作成するよう指示することが様々な面で便利です。テストコード作成の過程までだけ人間が見守ればよく、その後は比較的神経を使わなくても済むからです。実は、私はLLMコーディングエージェントを活用する際も時々テストを直接書くこともあります。プロンプトで要件を正確に検証するテストコードを書かせるよりも、私が直接テストコードを書く方が速いと感じる時にそうします。

このような作業の流れを好むようになったため、より厳密な型システムを備えたプログラミング言語、より厳格なリントルールなどがLLMコーディングエージェントを活用する際にはるかに有利だと考えるようになりました。LLM時代以前も考えは似ていましたが。

時には手コーディング

しかし、依然としてLLMには多くの限界があるため、私はまだ時々手コーディングを行います。API設計をする時もそうですし、厳密なテストコードを書きたい時もそうです。(LLMはテストコードをやや適当に書く傾向があります。)そして何よりも、面白そうなコーディングは自分でやります!

バイブコーディングに深く没頭した結果、コーディングの楽しさが失われたというソフトウェアプログラマーの話をよく耳にします。私の考えでは、楽しい部分はLLMに任せないほうが良いでしょう。結果の品質のためではなく、ソフトウェアプログラマーとしてのモチベーションを維持するためにそうするのです。面白くない退屈な部分、つまりコーディングしたくなくなるような作業で最大限LLMを活用することが、LLMと共存する良い戦略ではないかと思います。まあ、少なくとも私にはこの方法がうまく機能しているようです。


  1. これは一つのヒントですが、LLMだけに必要な情報を<!-- … -->コメント内に書く方法もあります。 ↩︎

  2. Shift + Tabを二回押すとプランモードに入ることができます。 ↩︎

  3. Claude Codeでは/modelコマンドを通じて選択できます。 ↩︎

32
0
1

No comments

If you have a fediverse account, you can comment on this article from your own instance. Search https://hackers.pub/ap/articles/01996130-95d5-77a8-be87-8d36706b0853 on your instance and reply to it.