君たちはCursorを本当に使えているか
はじめに
こんにちは。Builtoという会社で代表 & エンジニアをしている冨田です。
私たちはマネジメントとタスク管理を圧倒的にサポートするAIエージェントを開発しています。
開発にもAIをフル活用しており、そこで得られた知見を共有したいと思います。
具体的には、経験3年以上の現役ソフトウェアエンジニア(生成AIのない時代からコードを書いてきた方々)をターゲットに、本番運用レベルの大規模コードベースでもCursorを活用しコーディング時間を 1/3〜1/5 に縮めている手法をお伝えします。
仕様策定やアプリの機能にもLLMをフル活用していますが、今回は実装にフォーカスします!
(なお本記事は中級者以上向けのため、まだCursorに触れたことがない方はまず別の記事を参考にして、使ってみてください)
なぜこの記事を書いたか
現職エンジニアが本当に使いこなす手法を共有したい
すでに共有されているAI開発事例の多くは「ゼロイチの新規開発」や「TODOアプリ」のような小規模サンプルが多いです。しかし実際の現場で直面するのは、すでに大規模化した既存コードの新規機能追加やメンテナンスにどうAIを活かすか、という問題です。
私の周りでもCursorを導入しているエンジニアは多いものの、うまく使いこなせていないと感じるケースが少なくありません。
Cursor Composerの凄さを伝えたい
Cursorのうち後発のComposer機能は、変更を自動生成して反映まで自動で行うため、その効率の高さは驚異的です。私自身、9割のコードをComposerに書かせ、実装時間はかなり削減しています。その具体的手法をお伝えします。
もっと多くの方の開発事例を知りたい
この記事はあくまでも私の手法なので、もっと活用している事例などあればぜひコメントにて教えてください!
前提
具体的手法に入る前に、AI開発を進める上での前提から。
1. AIが一度のプロンプトで正確に実装できるコード量には、まだ限界がある
2025年2月現在のモデルの性能では、既存のアプリケーションの大規模機能開発を一発で完結させるには、以下の点でまだ難しいと考えています。
- モデル性能の限界
-
Claude 3.5 Sonnet
やGPT-4o
といった単一レイヤーのLLMでは、仕様や意図の把握には優れていますが、一度のプロンプトで数千行単位の詳細実装を指示してもそこまで処理できず、抜け漏れが発生します。 -
o1
やo3-mini
などのReasoningモデルなら大きく複雑な指示をそれなりに処理できますが、細かい指示が散逸する・既存実装を踏襲してくれないなどの問題があります。またレスポンスが遅かったり、Cursor側での最適化が十分ではないためコードが適切に更新されなかったりします。
-
- 既存コード固有の問題
- 大規模チームでは明示・暗黙問わずコーディング規約が存在します。(例: 弊社ではバックエンドでドメイン駆動設計・オニオンアーキテクチャを採用しています)
- ゼロイチ開発をAIにすべて行わせるならこの点は考慮不要ですが、多くの場合は、規約を厳守しつつ仕様を満たし、また負債が貯まらないような開発をする必要があり、まだエンジニアの高度な判断が必要です。
2. AI開発の際は、参考ファイルを常にプロンプトに含める必要がある
AI(LLM)が最も得意なのは、既存の設計や規約を参考に実装することです。とくに大規模な既存コードベースでは、エンジニア固有の命名規則やアーキテクチャ、ドメインモデリングなどがあります。
これらをプロンプトに含めないまま指示をしても、AIが想像でコードを書いてしまい、結局手直しに時間がかかってしまいます。
ClineやagentモードのCursor Composerはこのファイル参照を自動的に実現してくれるものですが、ファイル数が多いとどうも精度が低いと感じています。。
これらの前提を踏まえ、2025年2月現在のAI性能を最大限活用したCursorの開発手法を紹介します。
(というのも、数年しないうちにこれらの問題は解消されるだろうと予想していますw)
具体的手法
大きく分けて、以下の2点が鍵です。
- プロジェクトごとの事前設定を適切に行う
- 適切なプロンプト実装を行う
1. プロジェクトごとの事前設定を適切に行う
事前設定をしっかりと行うと、実装精度は劇的に高まります。
すべてのプロンプトに影響するため、ここの設定は時間をかけて作り上げ、適切に改善していくことをおすすめします。
(1) モデル選択
モデルは基本的にClaude 3.5 Sonnet
を選びます。
理由は、2025年2月時点で速度・精度のバランスの高さがトップクラスなためです。スコープを狭めた指示であれば、高い精度で素早く実装できます。
(少し前に、JavaScript系ならClaudeが、Python系ならGPTが相性がよい、という情報がありました。弊社はTypeScriptですが、比較してもClaudeのほうが良いコードを書きます)
要件自体が難解な指示をする際は、o1やo3を使う場合もあります。
(2)各設定
v0.45にて、私がオンにしている設定です。基本的にオンにして構わないと思います。
特に最近のアプデで良かったのは、以下です。
- Auto Import
- TypeScriptのファイルimportを提案してくれる
- Large context
- Composerに渡すファイルが多くなった場合にも対応できる
- Auto context
- Composerに渡すファイルをある程度自動で探してくれる(オンオフの比較まではできていませんw)
- Review changes
- Composerでのファイル変更を見やすくしてくれる
(3).cursorrules
.cursorrules
は、プロジェクトごとに共通で扱えるシステムプロンプト(各Chat, Composerで必ず渡される情報)です。
Git管理が可能なので、チームでも共有できる点が便利ですね。
Settingから設定できるRules for AI、NotePadsとの使い分けは以下にしています。
-
Rules for AI
- 全プロジェクトで共通の指示
- (私は、「Always respond in Japanese.」のみ記載しています)
- 自分個人のみが扱うような指示
- 全プロジェクトで共通の指示
-
.cursorrules
- プロジェクトごとに固有の情報
- アプリケーションの概要
- 技術スタック
- 実装時のベストプラクティス
- etc
- チームで統一して行いたい指示
- プロジェクトごとに固有の情報
-
NotePads
- より詳細な実装の方針指示
- 例: backendとfrontendのそれぞれのコーディング規約
- 踏襲すべき既存ファイルの参照
- より詳細な実装の方針指示
NotePadsについては後述します。
例えば、私のプロジェクトでの.cursorrulesは以下のようにしています。
あなたは高度な問題解決能力を持つAIアシスタントです。
後に示す指示に従って、効率的かつ正確にタスクを遂行してください。
■ このアプリケーションの概要
「〇〇」という、LLMを活用した新しいマネジメント支援・タスク管理のサービスです。
...(以降機密なので省略、主な機能や実現方法を説明します)
■ 主な技術スタック
- TypeScript
- Node.js
- Prisma
- AWS CDK
■ 実装時の注意点
- 常に既存コードの設計や記法を参考にしてください。
- TypeScriptにおいては、any型の使用は避けてください。
- クラスのmain関数が長くなる場合、適切な粒度でメソッドを分割してください。
- 書籍「リーダブルコード」のようなベストプラクティスを常に適用してください。
- コードの意図・背景などのコメントを各行に積極的に入れてください。また関数にはJSDocを入れることが推奨されます。
---
それでは、指示に従ってタスクを遂行してください。
<指示>
{{instructions}}
</指示>
ポイントは以下です。
- 「このサービスの概要」を伝えることで、大まかに何を実現するアプリケーションなのか、を伝えられる
-
チームで統一したい実装のベストプラクティスについて伝える
- 特にこれまで活用して何度も指摘したような内容を書いています。any型を用いたり、main関数が長くなったり、など
-
コメントを積極的に入れさせる
- コードの各行や関数の先頭には、かなり意識的にコメントを入れさせています
- これは人間の理解のためでもありますが、主にはAI開発を効率的に進めるためです
- 実装の背景や意図も含めてAIに読ませることで、後日に別プロンプトで改修をする際も、その意図を汲み取って実装を行ってもらえます
- 以前の人間のみの開発では、コメントが多すぎることのノイズのデメリットはありましたが、AI駆動開発においては、得られるメリットのほうが大きいと考えています
ちなみに.cursorrulesについては、共有サイトがあったり有志の方がまとめてくれたりしていますね。
(実装→検証・テスト→修正までを1サイクルで行う指示のようなものもあるのですが、数万行ある既存プロジェクトに対して、現状のモデルとAgent性能で、有用なレベルまで取り扱うことが難しかったです。私の能力不足もあると思います & 今後に期待です)
(4)NotePads
私が.cursorrulesより強力だと感じているのが、NotePadsです。
.cursorrulesとの違いとしては、以下です。
- @でのファイル参照ができる
- 毎回のプロンプトにて個別に渡すことができる
前述のようにAI開発の精度は、「いかに適切に参考ファイルを参照させるか」 にかかっています。
.cursorrulesではファイル参照ができないため、これをNotePadsで行います。
例えば、バックエンド実装用のNotePadは以下のようにしています。
バックエンドのディレクトリ構成と、実装の際に参考にしてほしい既存ファイルの例について説明します。
■ バックエンドのディレクトリ構成
設計思想として、ドメイン駆動開発とオニオンアーキテクチャを採用しています。
---
ai-agent/
├── src/
│ ├── cdk/ # AWS CDKのインフラ関連
│ ├── domain/
│ │ ├── dto/ # DTO (Data Transfer Object)
│ │ ├── entities/ # エンティティ
│ │ ├── query-services/ # クエリサービスのインターフェース
│ │ ├── repositories/ # リポジトリのインターフェース
│ │ ├── services/ # ドメインサービス(主に複数のユースケースから共通して呼ばれる処理)
│ │ ├── types/ # 型定義
│ │ └── values/ # 値オブジェクト
│ ├── infrastructure/
│ │ ├── query-services/ # クエリサービスの実体
│ │ ├── repositories/ # リポジトリの実体
│ │ ├── scripts/ # スクリプト
│ │ └── utils/ # ユーティリティ
│ ├── tests/
│ │ └── stubs/ # テスト用のスタブ
│ └── use-cases/ # ユースケース
└── utils/ # プロジェクト全体で使用するユーティリティ
---
■ 既存ファイルの例
常にこれらのファイルの記法を参考にして、実装を行いなさい。
DBスキーマ: @schema.prisma
Entity: @member.entity.ts @prompt.entity.ts @conversation.entity.ts
ValueObject: @member-id.value.ts @content.value.ts
Repository(インターフェース): @member.repository.ts @prompt.repository.ts @conversation.repository.ts
Repository(実体): @member.repository.ts @prompt.repository.ts @conversation.repository.ts
DTO: @conversation-history.dto.ts
QueryService(インターフェース): @conversation-history.query-service.ts
QueryService(実体): @conversation-history.query-service.ts
※DTO,QueryServiceは、多くのEntityを一度に用いる複雑なUseCaseにおいて、DBの取得処理を効率化するために用いる、取得専用のオブジェクトです。
UseCase: @execute-chat.use-case.ts @get-prompts.use-case.ts
Handler(UseCaseを呼び出す): @index.ts @index.ts @index.ts
常に上記のファイルを参考にしつつ、ドメイン駆動開発のベストプラクティスも適用しながら実装を行いなさい。
各@ファイルの部分は、実際に入力して補完を用いてセットしたものです。
このファイルを「backend」などの名前をつけて保存し、バックエンド実装時のプロンプトでは毎回コンテキストとして渡します。
こうすることで、既存のコーディング規約を保ったまま、ディレクトリも間違えずほぼ一発で実装を行ってくれます。
(NotePadsが出る前は毎回手打ちで指定していたので、本当に楽になりました。。)
バックエンドを例にしましたが、フロントエンドでも同様です。(例えばTailwindのクラスファイルなどを参照すれば、それを活用して実装してくれます。)
残念なことにNotePadsはGit管理できないので、チームでは各メンバーに設定してもらう必要ありますが、うまく活用することでかなり生産性が高まりました。
(5).cursorignore
.gitignoreと同様、コンテキストに必ず含めないファイル指定をします。
ビルドファイルなど不要なものが含まれると精度が落ちますし、機密情報を渡さないよう.env
はマストで指定したほうがよいです。
私の例です。
# .env
.env
.env.*
# node_modules
node_modules/*
# OSX
.DS_Store
# tmp
/tmp/*
!/tmp/.keep
# ESLint
.eslintcache
# testing
coverage/
# AWS CDK
.cdk.staging
cdk.out
# tmp
/tmp/*
!/tmp/.keep
(6)ショートカットコマンド等
私はComposerを多用するので、以下のコマンドをキーボードショートカットに追加しています。
-
composer.addfilestonewcomposer
: 現在エディタで開いているファイルを含めてComposerのチャットを開始する -
composer.addfilestocomposer
: 現在エディタで開いているファイルを、Composerのチャットに追加する
ここまでが私が行っている設定です。次に、開発ごとのプロンプトを実装していきましょう。
2. 適切なプロンプト実装を行う
プロンプト実装時、エンジニアの役割としては大きく2つあります。
1. 仕様の詳細と、実装のヒントを与える
2. 実装をステップに分け、順番に実行させる
600行規模のバックエンド機能を実装する場合を例に、説明します。
まず前提として、normalモードのComposerを用います。
agentモードを用いない理由としては、まだ実装精度が低いためです。
(前述で、o1系のReasoningモデルではなくClaude 3.5 sonnetを使う理由とも近いですが、Agenticな動作をさせると、せっかく伝えた実装内容の詳細が失われてしまうことが多々あり、結局手直しの実装が必要になってしまいます。またエラー解消の無限ループに陥り実装が終わらないこともあります…今後に期待!)
プロンプト実装
以下は仕様がシンプルな機能の例です。
新機能を作るので実装お願いします
# 仕様
- トップページで、ユーザーがプロンプトに対して、プロンプトにブックマークのピンを立てることができる
- 削除もできる
- トップページで「ピンで固定」をオンにしたら、ピンしたプロンプトのみ表示する
# IF
## ピン追加
POST /prompt/{promptId}/pin
### Input
{}
### Output
{}
200なら成功
## ピン削除
DELETE /prompt/{promptId}/pin
### Input
{}
### Output
{}
200なら成功
## プロンプト一覧
GET /prompts/?keyword=xxx&pin=true
クエリパラメータを追加する
- pin: true
- (実際はpinパラメータがあればピン済のものだけ返す形にする)
### Output
- pinパラメータがあれば、ユーザーがピンしたプロンプトだけ返す形とする
- スキーマは今と変わらない
# 関連ファイル
@prompt.repository.ts @get-prompts.use-case.ts @index.ts
---
以下のステップで進みたいです
1. Prismaスキーマ・Entityの実装
2. 各Repositoryの変更
3. ピン追加API(UseCase, handler)の実装
4. ピン削除API(UseCase, handler)の実装
5. プロンプト一覧API(UseCase, handler)の実装
それではステップ1からお願いします
今回は仕様がかなりシンプルですが、複雑な機能を作る際は、仕様欄にすべて詳細に書きます。(まさにPdMが仕様書を作る際と同様です)
また実装に関連するファイルはわかる範囲で渡せると良いです。
伝えたいポイントは、エンジニアが実装のステップを区切り、ステップを順々に行うよう指示するということです。
前述のように、AIが一度のプロンプトで正確に実装できるコード量には限界があります。
その閾値としては、大体300行くらいと考えてます。
つまり一度の実装量が大体300行以内になるよう、ステップを区切ると、かなり正確に実装してくれます。
ここで実装量でステップを考えるというよりは、エンジニアが機能実装時に必ず行うステップを想像するのが楽です。
弊社のバックエンド開発を例に取ると、ドメイン駆動開発を用いているので以下のようなステップにすることが多いです。
1. DBスキーマを変更し、EntityとValueObjectを実装する
2. Repositoryのインターフェースと実体を実装する
3. UseCaseを実装する
4. UseCaseを呼び出すhandlerを実装する
実際のプロンプトの流れとしては、以下です。
- 「(仕様詳細とともに)ステップ1から実装をお願いします」
- 結果を見てフィードバックし、必要に応じて修正・テスト実装などさせる
- okなら「Save all」
- 「次にステップ2に進みましょう」
- 結果を見てフィードバックし、必要に応じて修正・テスト実装などさせる
- okなら「Save all」
- ...
このサイクルを繰り返す形です。Composer登場前は、Chatで都度ファイル確認→手動でApplyする必要がありましたが、Composerは変更まで一気に行うためスピードが圧倒的です。
設定のReview changesをオンにすれば、変更も見やすいです。
新機能開発を例に説明しましたが、修正においても流れは同じです。
修正が多くなったときのTips
仕様を詳細に書く & ステップを分けることでほとんど修正なく実装できることが多いですが、
複雑な機能を実装する際は、どうしても「結果を見てフィードバックし、必要に応じて修正させる」の量が多くなることがあります。
ChatGPT等すべてのAIチャットに言えることですが、チャットの回数が多くなる(目安10ラリー以上)と、当初の指示内容を忘れてしまい精度が落ちます。
この際、ステップごとにチャットを新しくし、ここまでの差分を渡すのが効果的です。
以下の流れです。
- ステップの実装完了時に、Gitをcommitする(タイトルは実装内容にする)
- 新しいチャットを作成し、次のステップとして以下のようなプロンプトを入れる
{省略: 最初のプロンプトにて渡した仕様情報}
---
{省略: ステップの情報}
---
ステップ2まで完了しました。diffは以下です。
ステップ1: @DBスキーマとEntityを実装
ステップ2: @Repositoryの実装
では、ステップ3の実装をお願いします。
Cursorでは、Gitのコミット情報もコンテキストとして渡せます。
「ステップ1: @DBスキーマとEntityを実装」としている箇所は、@git と入力し、続いてコミットタイトルを入力すると、diffを注入できます。
こうすることで、プロンプトが新しくなってもここまでの実装内容を正確に伝え、続きの実装をさせることが可能です。
(他にも @コンテキスト をこう上手く使っている、のような情報あれば教えて下さい!)
ここまでやっても実装が意図通りにならない場合、設定不足・参考ファイル不足か、要件を明確に伝えられていないか、のどちらかです。両方ができているか、確認してみてください。
おわりに
Cursor Composerをフル活用して、すでに大規模化した既存コードを開発している手法を紹介しました。
実際この手法で、私個人の実装にかける時間は1/3〜1/5になっています。(その分QAが重要となるため、念入りに行う必要はありますが、全体でも相当短縮しています)
今後モデル性能の向上やCursor側のアップデートにより、ステップ分けが不要になったり、自動的で最適なファイル参照も行えるようになるでしょう。
またDevinのようなPR自動作成まで、Cursorも手掛けるとのことです。
この記事の手法の寿命としては今年までくらいと予想していますが、
現状でエンジニアが最もコストパフォーマンス高く実装できる手法だと思っていますので、ぜひお試しください。
またこのようにCursorや他サービスを活用している、このモデルだともっと精度高い、のような情報があれば、コメントやシェアいただけると嬉しいです!
Discussion
cursorrules はこちらのサイトも良さそうでした