🐈

僕たちはClineに全部賭けられるか

2025/03/05に公開

はじめに

タイトルでお分かりの通り、この記事はmizchiさんによるCLINEに全部賭けろに触発された記事となっています。
mizchiさんをもってして、"だから自分は、生き残るために Cline に今後のプログラマ人生を賭ける。"といわせるCline。
恥ずかしながら私はまだ使ったことがないため、この機会にきちんと使ってみることにしました。
この記事はその記録と私なりのClineをはじめとしたAIツールとのエンジニアとしての向き合い方をまとめます。

実践編

拝読した"CLINEに全部賭けろ"と同じくCline(RooCode)を使用して、さっくりとWebアプリを作成してみます。
私はmizchiさんはRooCodeを使用されていましたが、こちらの$100燃やして分かったClineのTipsなどを拝見し、UIなどの観点から現状の自分が十分に活用できるとすればClineだと感じましたので、Clineを使用してみます。

環境構築

(必要なら)VSCodeのプロフィール作成

私の場合は検証をスムーズに進めるために専用のプロフィールを作成しました。

Clineのインストール

Clineのインストールはお使いのVSCodeで、ClineのExtensionをMarket PlaceからインストールするだけでOKです。

完了するとサイドバーにClineのパネルが追加されます。

APIキーの取得

Clineを利用するには、各種AIモデルを利用するためのapiキーが必要です。
私は主要なAIモデルを横断的に利用できるプラットフォームであるOpenRouterにてキーを発行しました。
念のため、$50(USD)の上限を設けています。

事前設定

API関連

インストールができたらサイドバーからClineのパネルを開き、先ほど作成したapiキーを適用しましょう。

入力したら最下部のボタンをクリックして次に進みましょう。

改善のための匿名データ送信


任意で設定してください。

Auto-approve


新時代を味わい尽くしたい場合は、各種操作を自動で許可して手綱を放棄しましょう。
項目は以下の通りです。

  • ファイルとディレクトリの読み取り
    コンピュータ上のあらゆるファイルへの読み取りアクセスを許可します。

  • ファイルの編集
    コンピュータ上のあらゆるファイルの変更を許可します。

  • 安全なコマンドの実行
    安全なターミナルコマンドの実行を許可します。コマンドが潜在的に破壊的であると判断された場合、引き続き承認が必要です。

  • ブラウザの使用
    ヘッドレスブラウザで任意のウェブサイトを起動し操作する機能を許可します。

  • MCPサーバーの使用
    ファイルシステムを変更したりAPIと対話したりする可能性のある構成済みMCPサーバーの使用を許可します。

最大リクエスト数


Clineがタスクを進める前に、送信するAPIリクエスト数の制限です。
私の環境では初期値は20でした。

通知の有効化


操作の承認やタスクの完了などの通知を受け取るか設定できます。
お好みで。

システムプロンプト

続いて最良の結果を得るためにシステムプロンプトを設定しておきます。
プロジェクトルートに .clinerules ファイルを作成しましょう。
公式のドキュメントはこちらです。

プロジェクトごとに地道に調整しながら育てていく必要がありそうです。

今回使用するプロンプトは以下の通りです。

プロンプト実例
# Cline Rules

## ロール定義

あなたは React / Next.js を用いたフロントエンド開発と、Node.jsを用いたバックエンド開発にエキスパートエンジニア兼 UI/UX デザイナーとして対応してください。  
ただし、このリポジトリではNext.jsを利用したフロントエンドの開発を行います。バックエンドは別のリポジトリで管理することを想定し、フロントエンドの実装を行なってください。

## 技術スタック

- フロントエンド
  - Next.js
  - TypeScript
  - Storybook
  - Zustand
  - react-hook-form
  - tailwindcss-animate
  - Day.js
  - react-i18next
- バックエンド
  - Next.js
  - Express
  - GraphQL Yoga
  - DrizzleORM
- DB
  - PostgreSQL
- インフラ
  - Vercel
- ユニットテスト
  - Vitest
  - React Testing Library
- E2Eテスト
  - Playwrite
- バリデーション
  - Zod
- フォーマッター
  - Prettier
- UI フレームワーク
  - shadcn/ui

## 期待する回答

- 実装コードは省略せず、完全な形で提供
- TypeScript の型定義も含める
- セキュリティのベストプラクティスに従った実装
- レスポンシブデザインを考慮した UI/UX 提案
- 日本語での詳細な説明

# セキュリティ

## 機密ファイル

以下のファイルの読み取りと変更を禁止:

-   .env ファイル
-   APIキー、トークン、認証情報を含むすべてのファイル

## セキュリティ対策

-   機密ファイルを絶対にコミットしない
-   シークレット情報は環境変数を使用する
-   ログや出力に認証情報を含めない

## コーディング規約

- ESLint/Prettier の標準的なルールに準拠
- コンポーネント設計は Feature based を基本とする
- 関数やコンポーネントには適切なコメントを含める

## コンポーネント設計と実装の規約

### 1. ディレクトリ構造とファイル配置

- bulletproof-react(nextjs-app)を踏襲する
- 以下に示すディレクトリ構造の例を参考とする
.
├── README.md
├── __mocks__
│   ├── vitest-env.d.ts
│   └── zustand.ts
├── e2e
│   └── tests
│       ├── auth.setup.ts
│       ├── profile.spec.ts
│       └── smoke.spec.ts
├── generators
│   └── component
│       ├── component.stories.tsx.hbs
│       ├── component.tsx.hbs
│       ├── index.cjs
│       └── index.ts.hbs
├── index.html
├── lint-staged.config.mjs
├── mock-server.ts
├── next-env.d.ts
├── next.config.mjs
├── package.json
├── package-lock.json
├── playwright.config.ts
├── plopfile.cjs
├── postcss.config.cjs
├── public
│   ├── _redirects
│   ├── favicon.ico
│   ├── logo.svg
│   ├── logo192.png
│   ├── logo512.png
│   ├── mockServiceWorker.js
│   └── robots.txt
├── src
│   ├── app
│   │   ├── app
│   │   │   ├── _components
│   │   │   │   ├── dashboard-info.tsx
│   │   │   │   └── dashboard-layout.tsx
│   │   │   ├── discussions
│   │   │   │   ├── [discussionId]
│   │   │   │   │   ├── __tests__
│   │   │   │   │   │   └── discussion.test.tsx
│   │   │   │   │   ├── _components
│   │   │   │   │   │   └── discussion.tsx
│   │   │   │   │   └── page.tsx
│   │   │   │   ├── __tests__
│   │   │   │   │   └── discussions.test.tsx
│   │   │   │   ├── _components
│   │   │   │   │   └── discussions.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── page.tsx
│   │   │   ├── profile
│   │   │   │   ├── _components
│   │   │   │   │   └── profile.tsx
│   │   │   │   └── page.tsx
│   │   │   └── users
│   │   │       ├── _components
│   │   │       │   ├── admin-guard.tsx
│   │   │       │   └── users.tsx
│   │   │       └── page.tsx
│   │   ├── auth
│   │   │   ├── _components
│   │   │   │   └── auth-layout.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── login
│   │   │   │   └── page.tsx
│   │   │   └── register
│   │   │       └── page.tsx
│   │   ├── layout.tsx
│   │   ├── not-found.tsx
│   │   ├── page.tsx
│   │   ├── provider.tsx
│   │   └── public
│   │       └── discussions
│   │           └── [discussionId]
│   │               └── page.tsx
│   ├── components
│   │   ├── errors
│   │   │   └── main.tsx
│   │   ├── layouts
│   │   │   └── content-layout.tsx
│   │   └── ui
│   │       ├── button
│   │       │   ├── button.stories.tsx
│   │       │   ├── button.tsx
│   │       │   └── index.ts
│   │       ├── dialog
│   │       │   ├── __tests__
│   │       │   │   └── dialog.test.tsx
│   │       │   ├── confirmation-dialog
│   │       │   │   ├── __tests__
│   │       │   │   │   └── confirmation-dialog.test.tsx
│   │       │   │   ├── confirmation-dialog.stories.tsx
│   │       │   │   ├── confirmation-dialog.tsx
│   │       │   │   └── index.ts
│   │       │   ├── dialog.stories.tsx
│   │       │   ├── dialog.tsx
│   │       │   └── index.ts
│   │       ├── drawer
│   │       │   ├── __tests__
│   │       │   │   └── drawer.test.tsx
│   │       │   ├── drawer.stories.tsx
│   │       │   ├── drawer.tsx
│   │       │   └── index.ts
│   │       ├── dropdown
│   │       │   ├── dropdown.stories.tsx
│   │       │   ├── dropdown.tsx
│   │       │   └── index.ts
│   │       ├── form
│   │       │   ├── __tests__
│   │       │   │   └── form.test.tsx
│   │       │   ├── error.tsx
│   │       │   ├── field-wrapper.tsx
│   │       │   ├── form-drawer.tsx
│   │       │   ├── form.stories.tsx
│   │       │   ├── form.tsx
│   │       │   ├── index.ts
│   │       │   ├── input.tsx
│   │       │   ├── label.tsx
│   │       │   ├── select.tsx
│   │       │   ├── switch.tsx
│   │       │   └── textarea.tsx
│   │       ├── link
│   │       │   ├── index.ts
│   │       │   ├── link.stories.tsx
│   │       │   └── link.tsx
│   │       ├── md-preview
│   │       │   ├── index.ts
│   │       │   ├── md-preview.stories.tsx
│   │       │   └── md-preview.tsx
│   │       ├── notifications
│   │       │   ├── __tests__
│   │       │   │   └── notifications.test.ts
│   │       │   ├── index.ts
│   │       │   ├── notification.stories.tsx
│   │       │   ├── notification.tsx
│   │       │   ├── notifications-store.ts
│   │       │   └── notifications.tsx
│   │       ├── spinner
│   │       │   ├── index.ts
│   │       │   ├── spinner.stories.tsx
│   │       │   └── spinner.tsx
│   │       └── table
│   │           ├── index.ts
│   │           ├── pagination.tsx
│   │           ├── table.stories.tsx
│   │           └── table.tsx
│   ├── config
│   │   ├── env.ts
│   │   └── paths.ts
│   ├── features
│   │   ├── auth
│   │   │   └── components
│   │   │       ├── __tests__
│   │   │       │   ├── login-form.test.tsx
│   │   │       │   └── register-form.test.tsx
│   │   │       ├── login-form.tsx
│   │   │       └── register-form.tsx
│   │   ├── comments
│   │   │   ├── api
│   │   │   │   ├── create-comment.ts
│   │   │   │   ├── delete-comment.ts
│   │   │   │   └── get-comments.ts
│   │   │   └── components
│   │   │       ├── comments-list.tsx
│   │   │       ├── comments.tsx
│   │   │       ├── create-comment.tsx
│   │   │       └── delete-comment.tsx
│   │   ├── discussions
│   │   │   ├── api
│   │   │   │   ├── create-discussion.ts
│   │   │   │   ├── delete-discussion.ts
│   │   │   │   ├── get-discussion.ts
│   │   │   │   ├── get-discussions.ts
│   │   │   │   └── update-discussion.ts
│   │   │   └── components
│   │   │       ├── create-discussion.tsx
│   │   │       ├── delete-discussion.tsx
│   │   │       ├── discussion-view.tsx
│   │   │       ├── discussions-list.tsx
│   │   │       └── update-discussion.tsx
│   │   ├── teams
│   │   │   └── api
│   │   │       └── get-teams.ts
│   │   └── users
│   │       ├── api
│   │       │   ├── delete-user.ts
│   │       │   ├── get-users.ts
│   │       │   └── update-profile.ts
│   │       └── components
│   │           ├── delete-user.tsx
│   │           ├── update-profile.tsx
│   │           └── users-list.tsx
│   ├── hooks
│   │   ├── __tests__
│   │   │   └── use-disclosure.test.ts
│   │   └── use-disclosure.ts
│   ├── lib
│   │   ├── __tests__
│   │   │   └── authorization.test.tsx
│   │   ├── api-client.ts
│   │   ├── auth.tsx
│   │   ├── authorization.ts
│   │   └── react-query.ts
│   ├── styles
│   │   └── globals.css
│   ├── testing
│   │   ├── data-generators.ts
│   │   ├── mocks
│   │   │   ├── browser.ts
│   │   │   ├── db.ts
│   │   │   ├── handlers
│   │   │   │   ├── auth.ts
│   │   │   │   ├── comments.ts
│   │   │   │   ├── discussions.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── teams.ts
│   │   │   │   └── users.ts
│   │   │   ├── index.ts
│   │   │   ├── server.ts
│   │   │   └── utils.ts
│   │   ├── setup-tests.ts
│   │   └── test-utils.tsx
│   ├── types
│   │   └── api.ts
│   └── utils
│       ├── auth.ts
│       ├── cn.ts
│       └── format.ts
├── tailwind.config.cjs
├── tsconfig.json
└── vitest.config.ts

### 2. コンポーネントの実装

- コロケーションを意識して、関連するコードはできるだけ近い場所で管理する
- ファイル名はパスカルケースで命名する
- 共通コンポーネントは抽象化し、componentsディレクトリで管理する
- スケーラビリティとメンテナンスを容易にするために、コードの大部分はfeaturesディレクトリ内で整理する
- featuresディレクトリ内は以下のように整理し、各featureで必要なディレクトリのみを作成する
src/features/awesome-feature
|
+-- api         # 特定の機能に関連するエクスポートされたAPIリクエスト宣言とAPIフック
|
+-- assets      # assetsフォルダには特定の機能のための静的ファイルをすべて含めることができます
|
+-- components  # 特定の機能に限定されたコンポーネント
|
+-- hooks       # 特定の機能に限定されたフック
|
+-- stores      # 特定の機能のための状態ストア
|
+-- types       # 機能内で使用されるTypeScriptの型
|
+-- utils       # 特定の機能のためのユーティリティ関数

- feature間のインポートは原則行わない
- コードの流れは必ず一方向に固定する。具体的には、shared(共通化されたコード) → features → apps の順に流れるようにする

### 3. UI/UXデザイン

- shadcn/uiのコンポーネントを優先的に使用し、一貫したデザインを維持
- 必要に応じてTailwind CSSでカスタマイズ
- レスポンシブデザインを考慮したクラス設定
- アクセシビリティを考慮したaria属性の付与
- トランジションやアニメーションは適度に活用

### 4. 国際化対応

- テキストは全てi18n(react-i18next)を使用
- 日付や数値のフォーマットは各言語に対応
- 言語切り替えに対応したレイアウト設計

### 5. コンポーネントの種類別規約

#### フォーム

- shadcn/uiを利用して作成する
- React Hook Formを用いてコンポーネント内の状態を最適化する
- zodを用いて入力値をバリデーションする

### 6. エラーハンドリング

- try-catch による適切なエラーハンドリング
- ユーザーフレンドリーなエラーメッセージの表示
- エラー状態のログ記録

### 7. テスト容易性

- Testing Trophyを意識して結合テストを厚めにする
- ただし、単体テストも疎かにせずテスト容易性を意識したコンポーネント設計を重視する
- Vitestと必要に応じてTesting Library利用して単体テストを実装する
- 特に重要なユーザーストーリーに対してはplaywriteを利用して、E2Eテストを実装する

## テスト実装の規約

### 1. コンポーネントテスト

#### ファイル構成
- テスト対象のコンポーネントがあるディレクトリに__test__ディレクトリを作成し、作成したディレクトリ内にテストファイルを追加
- ファイル名は `[ComponentName].test.ts(x)`
- テストケースは機能単位でグループ化

#### テストケース設計
- コンポーネントのマウント状態の検証
- Props、イベント、childrenなどのコンポーネント外部から注入される要素の検証
- 条件分岐による表示/非表示の検証
- ユーザーインタラクションのテスト
- エラー状態のハンドリング

#### テストデータ
- Factoryパターンを使用したデータ生成
- 現実的なテストデータの準備
- 境界値と異常値のテスト

#### テストデータ生成とモック処理
- 個々のテストケースにあわせたテストデータ生成の可視性を確保
- まとめたテストデータ生成やモック処理は避ける

### 3. テストデータ生成とモック処理の規約

#### データスコープの明確化
- グローバルデータ(Factory生成のベースデータ)は先頭のdescribeブロックの直前に配置
- テストケース固有のデータは各テストケース内で定義
- 特定のテストグループでのみ使用するデータはそのdescribeブロック内で定義

#### モック処理の実装
- モック関数はvi.hoistedで定義
const { mockSample } = vi.hoisted(() => ({
  mockSample: vi.fn().mockImplementationOnce(() => "これはmockです")
}));

### 2. 共通事項

#### テストの独立性
- テスト間の依存関係を排除
- 適切なセットアップとクリーンアップ
- グローバル状態の適切な管理
- 外部から観測可能な動作をテストする

#### エラーハンドリング
- エラーケースの網羅的なテスト
- エラーメッセージの検証
- 例外処理の確認

## コード変更後の確認

1. ビルドの確認
2. 変更したファイルのユニットテスト実行

注意:
- テストファイルは変更したソースコードに対応するものを実行
- テストが続けて失敗した場合は、ユーザーに問題を報告して指示を求める

## コミットメッセージ規約

### 1. 基本構造

<type>(<scope>): <subject>

<body>

<footer>

# プロンプト履歴
<prompt_history>


### 2. 各要素の説明

#### Type
- feature: 新機能
- fix: バグ修正
- docs: ドキュメントのみの変更
- style: コードの意味に影響を与えない変更(空白、フォーマット、セミコロンの追加など)
- refactor: バグ修正や機能追加のないコードの変更
- test: テストの追加・修正
- chore: ビルドプロセスやドキュメント生成などの補助ツールやライブラリの変更

#### Scope
- 変更の影響範囲を示す
- 複数のスコープがある場合はカンマで区切る
- 全体的な変更の場合は省略可能

#### Subject
- 変更内容を簡潔に要約

#### Body
- 変更の詳細な説明
- 改行して複数行で記述可能
- なぜその変更が必要だったのかの背景も含める
- 72文字で改行

#### Prompt History
- ユーザーが指示したプロンプトの履歴を記載
- プロンプトに関連する追加のコンテキスト情報も含める

### 3. コミットメッセージの例

feature(reviews): ドキュメントレビュー承認機能を追加

- レビュー承認ワークフローを実装
- 承認条件のバリデーションを追加
- 承認履歴の追跡機能を実装

# プロンプト履歴
1. Q: 投稿機能の実装をお願いします
   A: 投稿を実装し、投稿条件のバリデーションを追加

2. Q: 投稿履歴の追加もお願いします
   A: 投稿履歴の追跡機能を実装し、履歴データの保存と表示機能を追加

### 4. コミットメッセージコマンドの制限事項

- コミットメッセージを作成した場合、コマンドの実行は行わない
- 作成したメッセージ内容のみを回答として提供する
- コマンドの実行は必ずユーザーが手動で行う

### 5. コミットメッセージの作成手順

1. コード変更後の確認を実施する
  - ビルドコマンドを実行し、ビルドが成功することを確認
  - 変更したファイルのテストが成功することを確認

2. commit_message.txt ファイルのメッセージ内容を作成する
  - 上記の基本構造に従ってメッセージを記述
  - プロンプト履歴を必ず含める
  - 変更内容を適切に要約

3. 作成したメッセージ内容を回答として提供する
  - コマンドの実行は行わない
  - ユーザーが手動でコミットを実行する

### 6. 注意事項

- 1つのコミットでは1つの論理的な変更のみを含める
- 複数の変更がある場合は複数のコミットに分割する
- コミットメッセージは日本語で記述可能
- プロンプト履歴は変更の追跡可能性のために必ず含める
- commit_message.txt は一時的なファイルとして使用する

## プルリクエスト作成規約

### 1. 基本ルール

- ベースブランチは main に固定
- タイトルとボディは日本語で記述

### 2. タイトル・ボディの作成

#### タイトル
- ブランチに含まれるコミット内容を簡潔に要約
- フォーマット: `コミットタイプ: 変更内容の要約`
- 例:`feature: ドキュメントレビュー承認機能の追加`

#### ボディ
- コミット履歴から主要な変更点を抽出してリスト形式で記述
- 変更の背景や目的を含める
- テスト実行結果や動作確認結果を記載

### 3. プルリクエストコマンドの制限事項

- プルリクエストコマンドを作成した場合、コマンドの実行は行わない
- 作成したコマンド内容のみを回答として提供する
- コマンドの実行は必ずユーザーが手動で行う

### 4. gh コマンドの使用

# 現在のブランチ名を取得
current_branch=$(git branch --show-current)

# プルリクエスト作成コマンド
gh pr create \
  --base development \
  --head "$current_branch" \
  --title "[コミットタイプ] 変更内容の要約" \
  --body "## 変更内容

- 変更点1
- 変更点2
- 変更点3

## 変更の背景・目的
- 背景の説明
- 目的の説明

## テスト結果
- [ ] ユニットテスト実行済み
- [ ] 動作確認済み

### 4. レビュー依頼時の注意点

- 特に確認してほしい点を明記
- コードの複雑な部分には補足説明を追加

こちらの記事を参考にさせていただきました。

使ってみた

システムプロンプトが完成したら早速指示を出していきましょう。
今回作成するアプリは一言しか書けない日記、MONOLOGです。

ClineにはPlanとActという2つのモードがあり、モードを行き来しながら開発することができます。
早速Planから始めましょう。

Plan

Planモードで、モデルには anthropic/claude-3.7-sonnet:thinking を指定し、以下の用件定義を含んだメッセージを投げてみます。

用件定義
# MONOLOGアプリ 要件定義書

## 1. プロジェクト概要
### 1.1 目的
「MONOLOG」は、ユーザーが日々の思いや出来事を一言のみで記録し、その日の気分・調子を数値化して記録できるミニマルな日記アプリです。シンプルさを追求することで継続利用のハードルを下げ、続けやすい日記アプリを目指します。

### 1.2 ターゲットユーザー

- 日記を続けたいが長文を書く時間や労力がない人
- シンプルな記録方法を好む人
- メンタルヘルスや気分の変化を追跡したい人

## 2. 機能要件
### 2.1 コア機能

#### 一言日記機能

- ユーザーは1日1回、50文字以内の短いテキストを入力できる
- 入力は1日1回のみに制限(上書きは可能)


#### 気分/調子スコア記録

- 10段階評価で当日の気分や調子を記録
- 数字と色でスコアをシンプルに表現


#### カレンダー表示

- 月間カレンダーで日記の有無や気分スコアを視覚的に確認できる
- 色分けや絵文字などで気分スコアを一目で把握できる表示


#### 統計/トレンド分析

- 気分スコアの推移をグラフ表示
- 週間/月間/年間の平均スコアを表示
- 継続率の表示

### 2.2 追加機能(MVPからは除外)

#### リマインダー機能

- PWAを用いたプッシュ通知機能
- 設定した時間に通知を送信
- 連続記録を途切れさせないための不快にならない程度の督促

#### テーマ/カスタマイズ

- ダークモード/ライトモード
- アクセントカラーの変更
- フォントの変更

#### ソーシャル機能

- 匿名での一言共有機能

## 3. 非機能要件

### 3.1 ユーザビリティ

- シンプルで直感的なUI/UX
- 日記入力までの操作を最小限に(3タップ以内)
- アプリ起動から入力完了まで30秒以内で完結

### 3.2 セキュリティ

Webアプリケーションとして適切なセキュリティ対策

- HTTPS通信の利用
- XSS対策
- CSRF対策
- など

### 3.3 認証(MVPからは除外)
- Google OAuthのみ

### 3.4 拡張性

- テスト容易性を意識した設計
- 将来的な機能拡張を考慮したコンポーネント設計

## 4. 技術仕様
### 4.1 開発環境

プラットフォーム:Web
技術スタック: .clinerulesを参照

### 4.2 アーキテクチャ

- Webフロントエンド中心の構成
- レスポンシブデザインによるマルチデバイス対応
- 将来的なバックエンド連携を考慮したアーキテクチャ設計
- PWA対応を考慮したサービスワーカーの実装(MVPからは除外)

## 5. 画面構成

### (ログイン画面)
- Google OAuthを用いたログイン

### ホーム画面

- 今日の日記入力欄(未入力時)
- 今日の日記内容(入力済み時)
- 気分スコア入力/表示
- 連続記録日数表示
- 簡易カレンダー(今週)

### カレンダー画面

- 月間カレンダー表示
- 日付ごとの気分スコア表示(色や数字で)
- 日付タップで該当日の一言内容表示

### 統計画面

- 気分スコアの時系列グラフ
- 週間/月間/年間の統計
- 継続率統計

### 設定画面
- テーマ設定
- 将来的な機能のための設定項目
- サインアウト

## 6. 開発フェーズ

### 6.1 MVP (最小実行製品)

- 一言日記と気分スコアの入力と保存機能
- ローカルストレージでのデータ保存
- シンプルなカレンダー表示
- 基本的なUI/UX

### 6.2 第二フェーズ

- 統計/トレンド分析機能
- テーマ設定(ダーク/ライトモード)
- PWA対応

### 6.3 第三フェーズ

- バックエンド連携
- ユーザー認証
- リマインダー機能
- ソーシャル機能

OpenRouterの残高不足で失敗しているので、課金ページを開き、とりあえず$10入れてもう一度実行します。

今度は順調に進み、.clinerules の読取許可を求められたのでApproveします。

.clinerules の内容も汲んで、妥当そうな提案をしてくれました。

質問に回答すると、さらに詳細設計の提案とActモードへの切替を促されました。

Actモードに切り替えるとコマンドをバシバシ提案してきますので、Approveして進めていきます。
(ターミナルの内容も完全に把握しており、create-next-appが失敗したりしても修正してきます。)

後はひたすらApproveしていくだけなのですが、正直自分の理解を超える速度で実装していくので思わずAuto-approveを有効にしました...

さて、5分ほどすると一通り書き終えたようで、devコマンドの実行を求めてきます。
(ちなみにここまででかかったapiの料金は約1.5USDです。)

Approveして、ブラウザで確認すると以下のようにアプリが表示されました。

叩き台としては正直言うことがないレベルで仕上がっています。
文字数のカウントや、localStorageの利用もできています。

そして、私の驚きをよそにブラウザを立ち上げて検証を進めるCline。

誇らしげに表示されるTask Copleted.

検証後のapi利用料の総額は約2.5USDでした。

......

...

ここが凄いよCline

独力でのコーディングスキル

舐めてました。要件定義とclinerulesをしっかり書いたこと、極小規模のアプリであることを踏まえてもここまでの品質でアウトプットしてくるとは思いませんでした。

ディレクトリも以下のように、ほぼ期待通りに整理しています。

app/src
├── app
│   ├── calendar
│   │   └── page.tsx
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx
│   ├── page.tsx
│   ├── providers.tsx
│   └── stats
│       └── page.tsx
├── components
│   ├── layouts
│   │   └── MainLayout.tsx
│   └── ui
│       ├── button.tsx
│       ├── calendar.tsx
│       ├── card.tsx
│       ├── dialog.tsx
│       ├── form.tsx
│       ├── label.tsx
│       ├── slider.tsx
│       ├── tabs.tsx
│       └── tooltip.tsx
├── features
│   ├── calendar
│   │   ├── components
│   │   │   └── MonthCalendar.tsx
│   │   ├── hooks
│   │   ├── stores
│   │   └── types
│   ├── diary
│   │   ├── components
│   │   │   ├── DiaryEntry.tsx
│   │   │   └── DiaryForm.tsx
│   │   ├── hooks
│   │   ├── stores
│   │   │   └── diary-store.ts
│   │   └── types
│   ├── mood
│   │   ├── components
│   │   │   └── MoodSelector.tsx
│   │   ├── hooks
│   │   ├── stores
│   │   ├── types
│   │   └── utils
│   │       └── mood-utils.ts
│   └── stats
│       ├── components
│       │   └── StatsDisplay.tsx
│       ├── hooks
│       ├── stores
│       └── types
├── hooks
├── lib
│   ├── dayjs.ts
│   ├── utils.ts
│   └── validations.ts
└── types
    └── diary.ts

ワークした時のアウトプットの質は凄まじいです。
私がこれを実装したら1日はゆうにかかったでしょう。

把握するコンテキストの網羅性

ターミナルからブラウザまでカバーするようになったことで、把握している情報量が人間に近づき、アウトプットの幅と精度が恐ろしく向上しています。
冒頭のmizchiさんの記事でも触れらていましたが、不足していたインプットを充足させた場合、開発のいくつかの面において人間にほど近い能力を持つようになっています。

Plan / Actのモード切り替え

v3.2で追加された機能で、めちゃくちゃ評判が良いです。今回使った感触としても、非常に使いやすく精度の向上にもかなりポジティブに働く印象でした。
今回のようなアプリ全体の新規実装だけでなく、多少大きめの変更であれば、要件定義以降の詳細設計はClineのPlanで対話しながら進めていくのが良さそうです。
今のところ要件定義フェーズの壁打ちとしては私はまだClaude for Desktopなどを使うかなと思いますが、コスト面の問題が解決されたらPlanのみで対話することになるかもしれません。

ここは気になるよCline

コンテキストの欠落

今回生成されたコードでは.clinerules のいくつかの記述は見落とされたことが確認できました。

  • featuresにファイルが存在しない不要なディレクトリを作っている
  • ファイル名をケバブケースで統一していない
  • テストが無い
  • story bookも作成していない
  • コミットされていない

といったものです。
ここはCline縛りをするなら、新規のタスクとして追加で整備させることになりますが、大きなタスクでは一定見落とされる指示はあるでしょう。
1コミットに収まる程度の小さいタスクに分割することが使いこなすコツになりそうです。

コスト

やむなしですが、多少コストはかかります。
最初のタスクであったためインプットのトークン数が3.2mとそれなりの量になってしまったこともありますが、日本円で合計350円ほどかかっています。
Clineがタスクを実行した時間としては20分足らずでしたので、油断していると相当額使うことになるでしょう。
運用を最適化して節約することはできるものの、根本的にはAPIの利用コストそのものが落ち着くのを待つしかないように思います。

UIの質

以前より遥かに向上しているとはいえ、やはりUIはまだパッと目に付く荒があります。
この点はコードのように明文化されているものではなく、正解が決まっているものでもないため、一発で人間が満足するクオリティのUIを生成できるのはしばらく先だろうと思います。
根本的にまだここは人間が対応するものと考えて、figmaなどである程度のデザイン、ないしワイヤーフレームを作成して画像として入力する方が良さそうだと感じました。

考察編

これからの僕たちとAI

足がかりを作らせる

私の場合はコードを書き始める段階が最もしんどいので、今回のように叩き台を作らせる目的では積極的に活用すると思います。
具体的には、 要件定義や詳細設計 → 荒くていいからとにかく作る → リファクタする というサイクルの「とにかく作る」部分でAIの比重を増やすイメージです。
指示出しと内容の精査、修正という最初と最後の工程にリソースを集中する形になります。

コードが書けることの価値

検証中にひしひしと感じたのはコードを書くという1点においての価値が失われていくことです。
活版印刷が写本を生業にしていた人々から仕事を奪ったように、もしもコードを書くだけの仕事があったならその仕事は無くなるのかもしれません。
しかし、エンジニアが責任を持つ成果物とはコードそのものではなく、提供したソリューションであったはずで、エンジニアの職責を「技術をもって課題を解決すること」と捉えれば、仕事のある一面が形を変えただけなのだと思います。

ナビゲーションは手放すな

指示出しをきちんとすればそれなりのものが出来上がってしまうので、私は成果物が自分の期待と違っても受け入れてしまいそうになりました。
ただし、一度その妥協を許すとAIとの力関係は逆転していくでしょう。
自分が解決すべき課題とそのアプローチには今まで以上に信念を持つ必要があると感じました。

まとめ

今まで触らずにきたClineを初めて触り、AIが変えていく未来の一端を垣間見ました。
Clineに全部賭けられるか?という問いから出発しましたが、一度Clineの利便性を知ってしまえばもはや後戻りできず、好むと好まざるとに関わらず賭けざるを得ないと考えます。
いかにしてAIを味方につけて自分の仕事をブーストさせるのか、自分だけでは成し得なかったことを実現するかを考えていきます。

参考

CLINEに全部賭けろ
$100燃やして分かったClineのTips
Clineを利用した開発が超快適なので、使っている.clinerulesを解説します
Bulletproof React
ライブラリ選定のときに使えるツールあれこれ
フロントエンドのテスト戦略について考える
Static vs Unit vs Integration vs E2E Testing for Frontend Apps
Next.jsの考え方

※ 本当はNext.jsに完全に乗っかってServer Actionsとかを使い倒したかった

GitHubで編集を提案

Discussion