Cursorで効率的にコーディングする方法
# Cursorで30日間コーディングしてみた——実際に効果のあった方法
100回は見たTypeScriptエラーの赤い波線を見つめながら、ネストされたジェネリクスでの`Omit`の正確な構文が思い出せない——そんな感覚をご存知ですか?3ヶ月前の私がまさにそうでした。Cursorを使い始めてから、複雑なプロジェクトではもう普通のVS Codeには戻れません。
でも、こういうことです:Cursorは魔法ではありません。最初の1週間は、高級オートコンプリートのように扱って、ゴミのような結果しか得られませんでした。30日間の意図的な練習の後、実際に時間を節約できるパターンと、時間を無駄にするパターンがわかりました。
## 重要なセットアップ
まず、基本を正しくしましょう。私はmacOSでCursor 0.45.xを実行していますが、同じ原則がWindows/Linuxにも適用されます。インストール後、ほとんどのチュートリアルがスキップする2つの設定をすぐに変更しました:
1. **インライン提案の「自動補完」を無効化**(設定 > エディター > Cursor > 補完:オフ)。常に表示されるゴーストテキストが、キー入力ごとに迷いを生んでいました。代わりに、必要なときだけ`Ctrl+K`でインライン生成をトリガーします。
2. **「編集を適用」モードを有効化**(Cmd+Shift+P > 「Cursor: 編集を適用の切り替え」)。これにより、Cursorが差分を表示するだけでなく、コードを直接変更できるようになります。大幅な時間短縮になります。
## 80/20の法則:実際に使う2つのコマンド
30日後、Cursorの使用は日常業務の80%をカバーする2つのコマンドに絞られました:
### 1. `Cmd+K`:「このブロックを修正」コマンド
これが基本中の基本です。コードブロックを選択し、`Cmd+K`を押して、Cursorに何をすべきか指示します。以下は、私が構築しているReactプロジェクトの実際の例です:
```typescript
// 私が書いたこの混乱を選択:
const handleSubmit = async (e) => {
e.preventDefault()
const data = new FormData(e.target)
const body = {}
for (let [key, val] of data.entries()) {
body[key] = val
}
// ...さらに50行の手動バリデーション...
}
```
「react-hook-formとzodバリデーションを使用してリファクタリングし、ローディング状態を処理し、エラーバウンダリを追加」と入力しました。
Cursorが4秒で生成したもの:
```typescript
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
type FormData = z.infer<typeof schema>
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
resolver: zodResolver(schema)
})
const onSubmit: SubmitHandler<FormData> = async (data) => {
try {
setError(null)
await api.login(data)
toast.success('ログインしました!')
} catch (err) {
setError(err instanceof Error ? err.message : 'ログインに失敗しました')
}
}
```
**コツ**:使用したいライブラリとパターンを具体的に指定すること。「リファクタリング」だけでは汎用的なコードが生成されます。「react-hook-formとzodを使用してリファクタリング」と指定すると、実運用可能なコードが生成されます。
### 2. `Cmd+L`:「これを説明」コマンド
実はこれが最も強力な機能です。馴染みのないコードベースやレガシースパゲッティコードを扱うときに常に使用しています。
例:`__getattr__`を理解できない方法で使用しているPythonスクリプトをデバッグしていました:
```python
class ConfigProxy:
def __init__(self, config):
self._config = config
def __getattr__(self, name):
if name.startswith('_'):
return super().__getattr__(name)
return self._config.get(name, None)
```
これをハイライトして、「このパターンを説明し、Pyright用に適切に型ヒントを付ける方法を示してください」と質問しました。
Cursorの応答は驚くほど詳細で、プロキシパターンを説明し、`__getattr__`が欠落した属性で無限再帰を引き起こすことを指摘し、`Protocol`を使用した型付きバージョンを提供しました:
```python
from typing import Any, Protocol
class ConfigProtocol(Protocol):
def get(self, key: str, default: Any = None) -> Any: ...
class TypedConfigProxy:
def __init__(self, config: ConfigProtocol):
self._config = config
def __getattr__(self, name: str) -> Any:
if name.startswith('_'):
raise AttributeError(name)
return self._config.get(name, None)
```
**コツ**:型付き言語を使用している場合は、常に「型ヒント」または「適切な型付け」を要求すること。Cursorは要求しない限り、型なしのコードをデフォルトで生成します。
## ワークフローを変えたパターン
新しい機能に対して私が今行っていること:
1. **関数を説明するコメントを書く**(実装ではなく)
2. **コメントをハイライト**して`Cmd+K`を押す
3. **生成されたコードを注意深くレビュー**——必ず少なくとも1つのエッジケースを見逃している
4. **フォローアップのプロンプトで反復**:「ネットワーク障害のエラー処理を追加」や「空の配列を処理するようにする」など
最近のプロジェクトの例:
```typescript
// このコメントを書いた:
// ユーザーデータを取得し、5分間キャッシュし、
// stale-while-revalidateパターンを処理する関数
// Cursorが生成したもの(3回の反復後):
const userCache = new Map<string, { data: User; timestamp: number }>()
const CACHE_TTL = 5 * 60 * 1000
async function getUser(id: string): Promise<User> {
const cached = userCache.get(id)
const isStale = cached && Date.now() - cached.timestamp > CACHE_TTL
if (cached && !isStale) {
return cached.data
}
// Stale-while-revalidate:古いデータを即座に返し、バックグラウンドで更新
if (cached && isStale) {
fetchUser(id).then(data => {
userCache.set(id, { data, timestamp: Date.now() })
})
return cached.data
}
const user = await fetchUser(id)
userCache.set(id, { data: user, timestamp: Date.now() })
return user
}
```
## Cursorが失敗する箇所(そして代わりにすべきこと)
3つの大きな問題点に直面しました:
**1. APIを幻覚することがある。** Cursorが、使用しているReactバージョンに存在しない依存配列を持つ`useMemo`フックを使用したコードを生成したことがあります。**修正**:常に生成されたコードを実際の依存関係のドキュメントと照合すること。
**2. 大規模なリファクタリングが苦手。** 5つのファイルにわたって「このReduxストアをZustandに変換」するよう依頼したところ、一貫性のないインポートと欠落したステートスライスの混乱が生じました。**修正**:リファクタリングは1ファイルずつ、ターゲットパターンについて明示的な指示とともに行うこと。
**3. プロジェクト固有の規約に苦戦する。** 私のチームはデータベースカラムに`snake_case`、TypeScriptに`camelCase`を使用しています。Cursorは一貫してどこでも`camelCase`を生成します。**修正**:プロジェクトのルートに`.cursorrules`ファイルを作成し、規約を追加する:
```
# .cursorrules
- TypeScript変数にはcamelCaseを使用
- データベースカラム名にはsnake_caseを使用
- .then()よりもasync/awaitを優先
- エラー処理:常に型付きエラーでtry/catchを使用
```
## 本当の生産性向上の秘訣
最大の時間節約はコード生成ではなく、**デバッグ支援**です。不可解なエラーメッセージが表示されたとき、それをCursorのチャット(`Cmd+L`)に関連コードブロックとともにコピーします。これで以下のような場合に何時間も節約できました:
- **TypeScript型エラー**:「型'X'は型'Y'に割り当て可能ではありません」→ Cursorが型の不一致を説明し、修正を提案
- **Reactレンダリング問題**:「最大更新深度を超えました」→ CursorがuseEffectの依存関係の無限ループを特定
- **API統合バグ**:「undefinedのプロパティを読み取れません」→ Cursorが非同期コードを追跡してnull参照を見つける
## 次のステップ
Cursorのすべての機能を一度に使おうとしないでください。以下が7日間の計画です:
**1〜2日目**:インラインコード生成に`Cmd+K`のみを使用。チャット、説明、コンポーザーは使わない。
**3〜4日目**:デバッグと馴染みのないコードの説明に`Cmd+L`を追加。まだコンポーザーは使わない。
**5〜7日目**:プロジェクトに`.cursorrules`を追加し、複数ファイル編集にコンポーザーを試す。
7日後には、実際に効果のあるパターンの筋肉記憶が身についているでしょう。そして、避けられない幻覚や悪いリファクタリングに遭遇したとき、Cursorを信頼すべき時と自分でコードを書くべき時が正確にわかるようになります。
さあ、避けていたファイルを開き、最も醜い関数をハイライトして、`Cmd+K`を押しましょう。未来の自分が感謝するはずです。