T-CREATOR

Cursor 前提の開発プロセス設計:要求 → 設計 → 実装 → 検証の短サイクル化

Cursor 前提の開発プロセス設計:要求 → 設計 → 実装 → 検証の短サイクル化

AI ペアプログラミングツール Cursor の登場により、従来の開発プロセスは大きく変わりつつあります。本記事では、Cursor を前提とした開発プロセス設計について、要求から検証までの短サイクル化を実現する具体的な方法を解説します。

従来の開発では、要求定義から実装、テストまでに長い時間がかかっていましたが、Cursor を活用することで、このサイクルを劇的に短縮できます。早速、どのように実現するのか見ていきましょう。

背景

従来の開発プロセスの課題

従来のソフトウェア開発では、ウォーターフォール型やアジャイル型のプロセスが主流でした。ウォーターフォール型では、要求定義、設計、実装、テストという各フェーズを順番に進めるため、フィードバックループが長くなりがちです。

アジャイル開発では短いスプリントで開発を回しますが、それでも各スプリント内では設計と実装に時間がかかり、検証までに数日から数週間を要することがありました。

mermaidflowchart TD
  req["要求定義<br/>(数日〜数週間)"] --> design["設計<br/>(数日〜数週間)"]
  design --> impl["実装<br/>(数週間〜数ヶ月)"]
  impl --> test["テスト・検証<br/>(数日〜数週間)"]
  test --> feedback{"要求を<br/>満たす?"}
  feedback -->|No| req
  feedback -->|Yes| done["完成"]

この図は、従来の開発プロセスにおける各フェーズの流れと、フィードバックループの長さを示しています。

従来のプロセスでは、以下のような問題がありました。

  • 要求から検証までのリードタイムが長い
  • フィードバックが遅れるため、手戻りのコストが高い
  • 開発者のコーディング時間が全体の大部分を占める
  • 設計書やドキュメント作成に時間がかかる

Cursor がもたらす開発環境の変化

Cursor は、AI を統合した次世代のコードエディタです。VSCode をベースとしながら、GPT-4 などの大規模言語モデルを活用して、コード生成、リファクタリング、バグ修正などを支援します。

Cursor の主な機能は以下の通りです。

#機能説明
1AI チャットコードベース全体を理解した上で質問に回答
2インラインコード生成コメントや指示からコードを自動生成
3コードの説明既存コードの動作を自然言語で説明
4リファクタリング支援コードの改善提案と自動実行
5バグ修正支援エラーメッセージから原因と解決策を提示

これらの機能により、開発者はコーディングそのものよりも、設計や検証、要求の理解に時間を使えるようになります。

課題

従来のプロセスが Cursor 時代に適さない理由

Cursor のような AI ペアプログラミングツールを導入しても、従来の開発プロセスをそのまま適用すると、その真価を発揮できません。

従来のプロセスは、コーディングに時間がかかることを前提に設計されています。そのため、詳細な設計書を事前に作成し、実装フェーズで長時間かけてコードを書き、最後にまとめてテストを行うという流れになっていました。

mermaidflowchart LR
  traditional["従来のプロセス"] --> slow["コーディングに<br/>時間がかかる"]
  slow --> upfront["事前に詳細な<br/>設計が必要"]
  upfront --> batch["まとめて実装し<br/>まとめてテスト"]
  batch --> risk["手戻りリスク大"]

この図は、従来のプロセスがコーディングの時間的制約に基づいて設計されていることを示しています。

しかし、Cursor を使うと、コーディング自体にかかる時間は大幅に短縮されます。むしろ、以下のような新しい課題が生まれます。

#課題内容
1要求の曖昧さAI に正確な指示を出すには、要求を明確にする必要がある
2検証の重要性増大生成されたコードが要求を満たすか、即座に確認する必要がある
3反復速度の向上短時間で何度も試行錯誤できるため、プロセスも高速化が必要
4ドキュメントの形骸化詳細な事前設計書よりも、動くコードで確認する方が効率的

AI 活用における新しいボトルネック

Cursor を活用した開発では、コーディング速度がボトルネックではなくなります。代わりに、以下の点が新しいボトルネックとなります。

要求の明確化

AI に適切な指示を出すためには、開発者自身が「何を作りたいか」を明確に理解している必要があります。曖昧な要求では、AI も曖昧なコードしか生成できません。

生成されたコードの検証

AI が生成したコードは、一見正しく見えても、実際には要求を満たしていない場合があります。そのため、即座に動作を確認し、フィードバックを得るプロセスが不可欠です。

反復のオーバーヘッド

Cursor を使えば短時間で多くのコードを生成できますが、その分、何度も試行錯誤を繰り返すことになります。従来のプロセスでは、この反復のオーバーヘッドが考慮されていません。

解決策

Cursor 前提の短サイクル開発プロセス

Cursor を最大限活用するためには、要求から検証までのサイクルを極限まで短縮したプロセスが必要です。ここでは、マイクロイテレーション と呼ぶアプローチを提案します。

マイクロイテレーションは、従来のスプリントよりもはるかに短い単位(数分から数時間)で、要求、設計、実装、検証を繰り返すプロセスです。

mermaidflowchart TD
  start["機能要求"] --> clarify["要求の明確化<br/>(数分)"]
  clarify --> design["軽量設計<br/>(数分)"]
  design --> prompt["Cursor への<br/>プロンプト作成<br/>(数分)"]
  prompt --> generate["コード生成<br/>(数秒〜数分)"]
  generate --> verify["即時検証<br/>(数分)"]
  verify --> check{"要求を<br/>満たす?"}
  check -->|No| refine["要求の精緻化"]
  refine --> prompt
  check -->|Yes| integrate["コードベースに統合"]
  integrate --> next["次の機能へ"]

この図は、Cursor 前提のマイクロイテレーションの流れを示しています。各ステップが数分単位で完結することに注目してください。

各フェーズの具体的なアプローチ

要求の明確化

要求を明確にするためには、以下のような問いに答えられる必要があります。

  • この機能は何を実現するのか?
  • 入力は何で、出力は何か?
  • どのような条件やエッジケースがあるか?
  • 既存のコードベースとどう連携するか?

これらの問いに答えることで、AI に対する指示も明確になります。

markdown# 要求明確化テンプレート

## 機能概要

- 何を実現するか: [1-2 文で記述]

## 入出力

- 入力: [データ型、形式、例]
- 出力: [データ型、形式、例]

## 制約条件

- [制約 1]
- [制約 2]

## エッジケース

- [ケース 1 とその対応]
- [ケース 2 とその対応]

このテンプレートを使うことで、要求を構造化して整理できます。

要求を明確化する際は、完璧を目指す必要はありません。むしろ、最小限の情報でまず試してみて、検証結果を見ながら要求を精緻化していくアプローチが効果的です。

軽量設計

Cursor を使う場合、詳細な設計書を事前に作成する必要はありません。代わりに、以下のような軽量な設計情報を準備します。

関数シグネチャ

typescript// 関数の入出力を型で定義
interface UserInput {
  name: string;
  email: string;
}

interface ValidationResult {
  isValid: boolean;
  errors?: string[];
}

function validateUser(input: UserInput): ValidationResult {
  // 実装は Cursor に任せる
}

関数シグネチャを定義することで、入出力の型が明確になり、Cursor に対する指示も具体的になります。

処理フローのコメント

typescriptfunction processOrder(order: Order): Result {
  // 1. 在庫チェック
  // 2. 価格計算
  // 3. 決済処理
  // 4. 在庫更新
  // 5. 確認メール送信
}

処理の流れをコメントで記述することで、Cursor が各ステップの実装を生成しやすくなります。

これらの軽量設計は、数分で作成できる上に、Cursor が理解しやすい形式になっています。

Cursor へのプロンプト作成

Cursor に効果的な指示を出すためには、プロンプトの書き方が重要です。以下のようなパターンが効果的です。

具体的な指示

typescript// ❌ 悪い例:曖昧な指示
// ユーザーを検証する関数を作って

// ✅ 良い例:具体的な指示
// UserInput型を受け取り、以下を検証する関数を作成:
// - nameが3文字以上20文字以下
// - emailが有効なメールアドレス形式
// - ValidationResult型を返す
function validateUser(input: UserInput): ValidationResult {
  // Cursorがここからコードを生成
}

具体的な指示を出すことで、Cursor は要求に沿ったコードを生成しやすくなります。

コンテキストの提供

typescript// 既存のユーティリティ関数
function isValidEmail(email: string): boolean {
  // 実装...
}

// このユーティリティを使って、ユーザー検証関数を実装
function validateUser(input: UserInput): ValidationResult {
  // isValidEmailを活用してください
}

既存のコードを参照させることで、コードベース全体で一貫性のあるコードが生成されます。

段階的な指示

複雑な機能は、一度に全てを指示するのではなく、段階的に実装していきます。

typescript// ステップ1: 基本的な検証
function validateUser(input: UserInput): ValidationResult {
  // まずnameとemailの基本的な検証を実装
}

// ステップ2: エッジケースの処理
function validateUser(input: UserInput): ValidationResult {
  // 空白文字、特殊文字などのエッジケースを追加
}

// ステップ3: エラーメッセージの改善
function validateUser(input: UserInput): ValidationResult {
  // より詳細なエラーメッセージを返すように改善
}

段階的に実装することで、各ステップで検証を行い、問題があれば早期に修正できます。

即時検証

Cursor が生成したコードは、即座に検証する必要があります。検証の方法は複数あります。

ユニットテストの自動生成と実行

typescript// Cursorに「この関数のテストを生成して」と指示
import { describe, it, expect } from 'vitest';
import { validateUser } from './validator';

describe('validateUser', () => {
  it('有効な入力を正しく検証する', () => {
    const input = {
      name: 'John',
      email: 'john@example.com',
    };
    const result = validateUser(input);
    expect(result.isValid).toBe(true);
  });

  it('無効な名前を検出する', () => {
    const input = { name: 'Jo', email: 'john@example.com' };
    const result = validateUser(input);
    expect(result.isValid).toBe(false);
    expect(result.errors).toContain(
      '名前は3文字以上である必要があります'
    );
  });

  it('無効なメールアドレスを検出する', () => {
    const input = { name: 'John', email: 'invalid-email' };
    const result = validateUser(input);
    expect(result.isValid).toBe(false);
    expect(result.errors).toContain(
      '有効なメールアドレスを入力してください'
    );
  });
});

テストコードも Cursor に生成させることで、実装とテストを同時に完成させられます。

対話的な動作確認

typescript// デバッグ用のサンプル実行コード
if (import.meta.vitest) {
  const { it, expect } = import.meta.vitest;

  it('サンプル実行', () => {
    console.log('--- 有効な入力 ---');
    console.log(
      validateUser({
        name: 'John',
        email: 'john@example.com',
      })
    );

    console.log('--- 無効な入力 ---');
    console.log(
      validateUser({ name: 'Jo', email: 'invalid' })
    );
  });
}

実際の出力をコンソールで確認することで、視覚的に動作を理解できます。

型チェックと Lint

bash# TypeScriptの型チェック
yarn tsc --noEmit

# ESLintによるコード品質チェック
yarn eslint src/

型エラーやリントエラーがないことを確認することで、コードの品質を保証します。

これらの検証を数分以内に完了させることで、問題があれば即座に修正のサイクルに入れます。

プロセス全体の統合

マイクロイテレーションを効果的に回すためには、以下のような環境整備が重要です。

#項目説明
1高速なテスト実行環境Vitest など高速なテストランナーの導入
2自動フォーマットPrettier などによる自動コード整形
3即座のフィードバックホットリロード、ウォッチモードの活用
4バージョン管理の細分化小さな変更ごとにコミット
5ドキュメントの自動化コードコメントから自動生成

これらの仕組みを整えることで、マイクロイテレーションを滞りなく回せます。

具体例

実際の開発フローのウォークスルー

ここでは、「ユーザー登録フォームのバリデーション機能」を例に、Cursor 前提の開発プロセスを実際に進めてみましょう。

ステップ 1: 要求の明確化(約 3 分)

まず、要求を明確にします。

markdown# 機能概要

ユーザー登録フォームの入力値をバリデーションする

# 入出力

- 入力: { username: string, email: string, password: string }
- 出力: { isValid: boolean, errors: Record<string, string[]> }

# 制約条件

- username は 3〜20 文字の英数字とアンダースコア
- email は有効なメールアドレス形式
- password は 8 文字以上、大小英字・数字・記号を各 1 文字以上含む

# エッジケース

- 空文字列
- 先頭・末尾の空白
- SQL インジェクション対策(エスケープ処理)

この段階では、完璧な要求仕様書を作るのではなく、最低限必要な情報を整理します。所要時間は約 3 分です。

ステップ 2: 軽量設計(約 2 分)

次に、型定義と関数シグネチャを準備します。

typescript// types.ts
export interface RegistrationInput {
  username: string;
  email: string;
  password: string;
}

export interface ValidationErrors {
  [field: string]: string[];
}

export interface ValidationResult {
  isValid: boolean;
  errors: ValidationErrors;
}

型定義により、入出力の構造が明確になりました。

typescript// validator.ts
import type {
  RegistrationInput,
  ValidationResult,
} from './types';

export function validateRegistration(
  input: RegistrationInput
): ValidationResult {
  // Cursorに実装を依頼する
}

この軽量設計により、Cursor が何を実装すべきか明確になります。

ステップ 3: Cursor へのプロンプトとコード生成(約 5 分)

Cursor のチャット機能を使って、以下のように指示します。

markdownvalidateRegistration関数を実装してください。

要件:
1. usernameのバリデーション
   - 3〜20文字
   - 英数字とアンダースコアのみ
   - 前後の空白は削除してから検証

2. emailのバリデーション
   - 有効なメールアドレス形式
   - 前後の空白は削除してから検証

3. passwordのバリデーション
   - 8文字以上
   - 大文字、小文字、数字、記号を各1文字以上含む

4. エラーメッセージは日本語で、配列で返す

5. 全てのフィールドが有効な場合のみisValid: trueを返す

Cursor が以下のようなコードを生成します。

typescript// validator.ts
import type {
  RegistrationInput,
  ValidationResult,
} from './types';

export function validateRegistration(
  input: RegistrationInput
): ValidationResult {
  const errors: ValidationErrors = {};

  // usernameのバリデーション
  const username = input.username.trim();
  const usernameErrors: string[] = [];

  if (username.length < 3 || username.length > 20) {
    usernameErrors.push(
      'ユーザー名は3〜20文字である必要があります'
    );
  }

  if (!/^[a-zA-Z0-9_]+$/.test(username)) {
    usernameErrors.push(
      'ユーザー名は英数字とアンダースコアのみ使用できます'
    );
  }

  if (usernameErrors.length > 0) {
    errors.username = usernameErrors;
  }

  // emailのバリデーション(次のコードブロックに続く)
}

生成されたコードの email バリデーション部分です。

typescript// emailのバリデーション
const email = input.email.trim();
const emailErrors: string[] = [];

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
  emailErrors.push(
    '有効なメールアドレスを入力してください'
  );
}

if (emailErrors.length > 0) {
  errors.email = emailErrors;
}

email の検証では、正規表現を使ってメールアドレスの形式をチェックしています。

typescript// passwordのバリデーション
const password = input.password;
const passwordErrors: string[] = [];

if (password.length < 8) {
  passwordErrors.push(
    'パスワードは8文字以上である必要があります'
  );
}

if (!/[A-Z]/.test(password)) {
  passwordErrors.push(
    'パスワードには大文字を1文字以上含める必要があります'
  );
}

if (!/[a-z]/.test(password)) {
  passwordErrors.push(
    'パスワードには小文字を1文字以上含める必要があります'
  );
}

if (!/[0-9]/.test(password)) {
  passwordErrors.push(
    'パスワードには数字を1文字以上含める必要があります'
  );
}

if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
  passwordErrors.push(
    'パスワードには記号を1文字以上含める必要があります'
  );
}

if (passwordErrors.length > 0) {
  errors.password = passwordErrors;
}

password の検証では、複数の正規表現を使って各条件をチェックしています。

typescript  // 結果を返す
  return {
    isValid: Object.keys(errors).length === 0,
    errors,
  };
}

全てのバリデーションが完了したら、エラーがない場合のみ isValid: true を返します。

コード生成にかかる時間は数秒から数分程度です。

ステップ 4: テストコードの生成と検証(約 5 分)

次に、Cursor に「この関数のテストコードを生成して」と指示します。

typescript// validator.test.ts
import { describe, it, expect } from 'vitest';
import { validateRegistration } from './validator';

describe('validateRegistration', () => {
  it('全て有効な入力の場合、isValid: trueを返す', () => {
    const input = {
      username: 'john_doe',
      email: 'john@example.com',
      password: 'Password123!',
    };

    const result = validateRegistration(input);

    expect(result.isValid).toBe(true);
    expect(result.errors).toEqual({});
  });

  it('usernameが短すぎる場合、エラーを返す', () => {
    const input = {
      username: 'jo',
      email: 'john@example.com',
      password: 'Password123!',
    };

    const result = validateRegistration(input);

    expect(result.isValid).toBe(false);
    expect(result.errors.username).toContain(
      'ユーザー名は3〜20文字である必要があります'
    );
  });
});

基本的なテストケースが生成されました。さらに追加のテストケースも生成させます。

typescriptdescribe('validateRegistration - エッジケース', () => {
  it('前後に空白があるusernameを正しく処理する', () => {
    const input = {
      username: '  john_doe  ',
      email: 'john@example.com',
      password: 'Password123!',
    };

    const result = validateRegistration(input);

    expect(result.isValid).toBe(true);
  });

  it('無効な文字を含むusernameを検出する', () => {
    const input = {
      username: 'john-doe',
      email: 'john@example.com',
      password: 'Password123!',
    };

    const result = validateRegistration(input);

    expect(result.isValid).toBe(false);
    expect(result.errors.username).toContain(
      'ユーザー名は英数字とアンダースコアのみ使用できます'
    );
  });

  it('複数のバリデーションエラーを同時に検出する', () => {
    const input = {
      username: 'a',
      email: 'invalid-email',
      password: 'weak',
    };

    const result = validateRegistration(input);

    expect(result.isValid).toBe(false);
    expect(result.errors.username).toBeDefined();
    expect(result.errors.email).toBeDefined();
    expect(result.errors.password).toBeDefined();
  });
});

エッジケースのテストにより、様々な入力パターンで動作を検証します。

テストを実行します。

bashyarn vitest run validator.test.ts

テストが全て通れば、実装が要求を満たしていることが確認できます。所要時間は約 5 分です。

ステップ 5: フィードバックと改善(約 3 分)

テストを実行した結果、いくつかの問題が見つかったとします。

yamlFAIL  validator.test.ts
   全て有効な入力の場合、isValid: trueを返す
   passwordに記号が含まれていない場合を検出する
    - Expected: パスワードには記号を1文字以上含める必要があります
    - Received: (エラーなし)

問題が見つかったら、Cursor に修正を依頼します。

passwordのバリデーションで記号のチェックが動作していません。
正規表現を確認して修正してください。

Cursor が修正したコードを生成します。

typescript// 修正後のpasswordバリデーション
if (
  !/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)
) {
  passwordErrors.push(
    'パスワードには記号を1文字以上含める必要があります'
  );
}

正規表現の特殊文字をエスケープすることで、正しく動作するようになりました。

再度テストを実行して、全てのテストが通ることを確認します。これで 1 つのマイクロイテレーションが完了です。所要時間は約 3 分でした。

ステップ 6: 統合と次のイテレーション(約 2 分)

検証が完了したら、コードをコードベースに統合します。

bash# 変更をステージング
git add src/validator.ts src/validator.test.ts src/types.ts

# コミット
git commit -m "feat: ユーザー登録フォームのバリデーション機能を追加"

小さな変更ごとにコミットすることで、問題があった場合に容易にロールバックできます。

次のイテレーションでは、さらに機能を追加したり、パフォーマンスを改善したりできます。

diff次のタスク:
- データベースでのusername重複チェック
- パスワード強度のスコア表示
- リアルタイムバリデーション

このように、マイクロイテレーションを繰り返すことで、段階的に機能を完成させていきます。

プロセス全体の所要時間まとめ

以下の表は、各ステップにかかった時間をまとめたものです。

#ステップ所要時間
1要求の明確化約 3 分
2軽量設計約 2 分
3プロンプトとコード生成約 5 分
4テスト生成と検証約 5 分
5フィードバックと改善約 3 分
6統合約 2 分
-合計約 20 分

従来のプロセスでは同じ機能を実装するのに数時間から数日かかっていたことを考えると、劇的な短縮です。

複数機能の並行開発

Cursor を使ったマイクロイテレーションの利点は、複数の機能を並行して進められることです。

mermaidflowchart LR
  subgraph iter1["イテレーション1<br/>(20分)"]
    req1["要求1:<br/>バリデーション"] --> impl1["実装・検証"]
  end

  subgraph iter2["イテレーション2<br/>(15分)"]
    req2["要求2:<br/>DB連携"] --> impl2["実装・検証"]
  end

  subgraph iter3["イテレーション3<br/>(10分)"]
    req3["要求3:<br/>UI統合"] --> impl3["実装・検証"]
  end

  impl1 --> iter2
  impl2 --> iter3
  impl3 --> done["機能完成"]

この図は、複数のマイクロイテレーションを連続して実行し、段階的に機能を完成させていく流れを示しています。

各イテレーションが独立しているため、途中で優先順位を変更したり、別の機能に切り替えたりすることも容易です。

まとめ

Cursor のような AI ペアプログラミングツールの登場により、開発プロセスの根本的な見直しが必要になっています。従来のプロセスは、コーディングに時間がかかることを前提に設計されていましたが、Cursor を使えばコーディング自体は数分で完了します。

そのため、新しいボトルネックは「要求の明確化」と「検証」になります。本記事で提案したマイクロイテレーションのアプローチでは、以下のような短サイクルで開発を進めます。

  • 要求の明確化: 約 3 分
  • 軽量設計: 約 2 分
  • Cursor へのプロンプトとコード生成: 約 5 分
  • テスト生成と検証: 約 5 分
  • フィードバックと改善: 約 3 分
  • 統合: 約 2 分

合計約 20 分で、1 つの機能を要求から検証まで完結させられます。これは従来のプロセスと比べて、10 倍から 100 倍の速度改善です。

このプロセスを成功させるためには、以下の点が重要です。

  • 完璧な事前設計よりも、小さく試して検証する姿勢
  • Cursor に対する具体的で明確な指示
  • 即座に検証できる環境の整備
  • 小さな変更ごとのコミットとフィードバック

Cursor 前提の開発プロセスを導入することで、開発速度が大幅に向上するだけでなく、より高品質なソフトウェアを作れるようになります。ぜひ、皆さんのプロジェクトでも試してみてください。

関連リンク