この記事は何
Fractalium という、Web でフラクタルを描けるサービスを Rust + WebAssembly で作成しました。
https://qiita.com/halhorn/items/9436ff1af29bb6e7f0ec
この記事では、このサービス開発に用いた、仕様駆動開発 x AI コーディング周りの実例を紹介します。
Fractalium
フラクタルを作成できるサイトです。
既存のフラクタルを手軽に改造したりもできるので、ぜひ遊んでみてください。
基本的な構成
オープンソースです。
Rust と、ゲームエンジンである Bevy で実装し、WebAssembly に書き出して公開しています。
開発にあたっては、初期は Claude Code(Opus、Sonnet)、その後は Cursor(主に Composer 2)を使用しています。
Rust について
Rust は、強力な型システムとメモリ安全性、そして C/C++ 並みの実行速度を備えたプログラミング言語です。
所有権などのやや難解な概念を使いこなさないといけないため、とっつきにくく、コンパイルを通すまでは大変です。しかし、コンパイルさえ通ってしまえば、特にメモリ管理において、安全に、そして高速に実行できる言語です。
また、Bevy のようなゲームエンジンがあったり、WebAssembly 向けにビルドできたりするので、今回のようなフラクタルのシミュレーションを作成して公開することにも向いています。
特にフラクタルでは、できるだけ大きな depth まで描画しようとすると、相応の実行速度やメモリ効率が求められます。これも、Rust を Fractalium 開発の言語に選んだ理由です。
ちなみに私は Rust は書けません。 会社の勉強会で部分的に The Rust Programming Language 日本語版 を読んでいたので、所有権その他のざっくりした概念は知っているよ、という程度です。
(ちなみに、厳密な型や所有権周りの話はとてもおもしろかったので、Rust 自体は大好きです。)
AI コーディングにおいては、AI が「自分の作ったものが誤っている」といかに知れるかが大事です。したがって、型やメモリ管理において正しいコードを書かないとコンパイルが通らない Rust の特徴は、AI コーディング時代の強力なガードレールとなり得ます。
仕様駆動開発
AI を使った開発では、本業でも使っている仕様駆動開発を使っています。
また、AWS さんが提唱している AI-DLC(AI 駆動開発)も参考にしています。
思想としてはできるだけアジャイルな開発に寄せています。
仕様駆動開発と AI-DLC
仕様駆動開発では、最初に仕様を自然言語(日本語など)で書いてから、それに従って AI に実装をさせます。
また、AI-DLC では開発全体のフェーズを以下の 3 つに分けます。
-
Inception: 企画的な設計
- 何を作るのかを AI を使って明確化していくフェーズ
- ビジネス的な要件からユーザーストーリーを作成
- 各ストーリーの仕様を詰める
- この段階では実装的な設計は入らない(はず)
- チーム開発する場合、このフェーズはモブプロすると良いらしい
-
Construction: 開発・実装
- Inception で作成した仕様に従って、具体的な実装上の設計やコードに落とし込む
-
Operation: 運用基盤作成
- デプロイの基盤整備
- インフラの構築
仕様駆動開発をする場合でも、大きめの実装になりそうであれば、上記の 3 フェーズを意識すると良いと思います。
例えば一番最初の MVP 作成など、作るものが曖昧な段階では Inception を入れて、企画的な仕様を AI と壁打ちしながら作成します。
ある程度機能が組み上がってきて、具体的な追加機能を入れる場合であれば、Inception と Construction を分ける必要はなさそうに感じています。
計画を立てる Skill
Inception、Construction に限らず、私が仕様駆動開発を行うときに使っている自前の Skill がこちらです。
# plan(planning 実装計画)
## いつこのスキルを使うか
- `/plan` や「planning に計画を書いて」と依頼されたとき
- `planning/**` 配下の計画ドキュメントを新規作成・更新するとき
---
## ドキュメントの章立て
各計画ファイルに次を含める(必要なら章を追加可)。
| 章 | 内容 |
|----|------|
| **目的** | なぜ必要か、何のためのタスクかを簡潔に |
| **計画概要** | 大まかなフロー。Phase N / Task N / Todo N などで段階化。細部は別ファイルへ。人間が素早く読むことを目的とする。 |
| **計画詳細** | 概要に書くと長すぎる内容のみ。AI が作業する際の詳細コンテキストとして使う。不要なら省略。 |
| **受け入れ条件** | AI が完了を検証できる条件。人がチェックする項目 |
**ファイル分割**: 例)`overall_plan.md` に Phase 一覧 → `phase1_foo.md` に Task 一覧。深い階層はファイルで分ける。
---
## 計画の立て方(手順)
- **抽象 → 具体**: 全体の大きな流れを先に書き、その後で詳細を足す。
- 全体を以下の粒度で切り分け、ディレクトリやファイルで階層構造を持たせる。作っているプロダクトや作業によって、必ずしもこのすべての階層が必要なわけではない。
- Feature: 特定の機能の実装。必ずディレクトリを切る。
- Phase: 機能を実装するためのフェーズ。ファイルもしくはディレクトリを切る。
- Task: そのフェーズを実装するための小さなタスク
- Todo: タスクの中のやること
- 詳細は後で考える。例えば Feature を作るときには Phase の概要のみを書き、Phase の詳細や Task は書かない。
- 各階層の md は、子階層の項目については 1 行程度の概要のみを書く。詳細は別ファイルに分ける。
- **壁打ちはチャット**: 未確定の議論は会話で行い、**確定した内容だけ**を md に反映する。
- **情報源**
- 外部ライブラリ、MCP の context7、および公式サイトなどの Web 検索。
- 実際の動きをコードから確認する。
---
## 記述ルール
- **コードは書かない**。最も詳しくても public メソッド名・シグネチャ・責務レベルまで。
- **最初から詰めすぎない**。詳細はその Phase/Task に着手する直前で決める。ただし「詳細次第で全体が変わる」場合だけ事前に詰める。
- **判断待ち**: `[Question]...[/Question]` で人に聞く。`[Draft]` に回答候補。人は `[Answer]` の後に記入。
- **初見で読める md のみ**: 初めてこの計画を見る人が理解しやすいことを目指す。経緯・議事録は残さない。決まった事実だけを構造化する。
- **簡潔に**: 文章は簡潔に書く。重複しないようにする。
---
## 受け入れ条件の書き方
- 各項目はチェックボックスにする。
- 各 Phase / Task ごとに、**検証可能な**完了条件を列挙する(テスト観点・確認手順・成果物の存在など)。
- 完了しているかの状態管理用のチェックボックスと、AI が自己確認できる条件を分けてもよい。
---
## 作業場所
- ユーザールールに従い、計画の置き場は `planning/{タスク名}/`(サブタスクならサブディレクトリ)。
要点と狙いは以下のとおりです。
-
階層化する
-
Feature / Phase / Task / ... と、大きなものから小さなものへと階層化
-
「XX できる」という大きな Feature を、それを実現するためのフェーズ、さらに細かいタスク、と分解できる
-
最初から詳細度 MAX で計画しても、計画は途中で変わる。アジャイルの原則に従い、直近作るものを細分化し、将来作るものは粗いままにする
-
細かいタスクやそのデバッグごとにコンテキストを分ける
- 細かいタスクをやっているうちにコンテキスト長を使い切って、その後やるつもりだったことを忘れる、みたいな AI あるあるを防止できる
-
Feature レベルのみ人間が見る、などで人間の監視レベルを切り替えられる
-
Inception では Feature までが対象となる
-
-
概要と詳細を分ける
- 概要は人間が読むもの。AI が何をしようとしているかを素早くキャッチアップするためのもの。
- 詳細は AI が読むもの。より計画を詳細化する際に覚えておかないといけないこと(大まかな計画をした時点での想定)などが入る
- 私は文章を読むのが嫌いなので、概要と詳細を分けることで、概要のみを追えば良い状態にしている
-
受け入れ条件
- 計画を立てる AI が想定したものが完了しているかを、作業をする AI に強制できる
- 人間に確認してほしいことなども書いてくれることが多いので、その意味でも有益
-
初めてこの計画を見る人が理解しやすいことを目指す
-
AI と議論をして仕様を修正していると、「XX は行わない」「XX は YY にした」などの経緯情報がどんどん入ってきて仕様の可読性が下がる
-
常に、初めて見る人がわかること、つまり経緯ではなく最終的に決まった仕様を中心に書くことを大事にする
- 直感に反する仕様になっている場合などは、経緯があっても良い
-
-
計画は planning に書き出し、残す
-
ワークスペース内(Fractalium の場合はリポジトリ内)に書き出すことで残る
-
これは良し悪しがある
-

- 壮大な計画の続きをやりやすい
- 過去の計画を読んで空気を読んでくれることもある
-

- 計画はその時点での計画なので、古いものは現状の仕様と異なる
- 古い計画を読んで AI が勘違いするかもしれない(意外とそのようなことになることはほぼないが)
-
-
MVP の作成と計画
開発はアジャイルに行います。
したがって、まずはフラクタルを作成できる最低限の機能を備えた MVP(Minimum Viable Product)の作成を目指します。
上記の Skill を使いつつ壁打ちして、実際にできた計画がこちら。
まずは AI と壁打ちして、作ろうとしているものは何なのか、MVP に必要なものは何なのかを決めていきましょう。Inception フェーズです。
もう記憶が曖昧ですが、Claude とチャットで何ができるべきかを詰めていって、最終的にユーザーストーリー以前の基盤部分も含めて overall_plan に書き出した記憶があります。
### Foundation(基盤)
| ID | 概要 |
|----|------|
| F1 | プロジェクト初期化(ビルドツール・型・lint・整形・テスト・README) |
| F2 | アプリ骨組み(操作部と結果表示部のレイアウト・ルーティング不要の単一ページ) |
| F3 | 正規化座標キャンバスコンポーネント([-1, 1] × [-1, 1] 正方領域、画面サイズ追従) |
| F4 | フラクタル状態モデル(基本図形・複製ルール・深さ)と状態管理層 |
### Features(フィーチャー)
各 Feature は `mvp/features/<feature_name>/` 配下にディレクトリを切り、必要なら詳細計画を追加する(着手直前で良い)。
| ID | 概要 |
|----|------|
| Feat1 | 基本図形の描画: ドラッグで直線を引け、複数の直線が基本図形として保持される |
| Feat2 | 複製の配置: 位置・角度・スケールを持つ複製を 1 つ以上配置・編集・削除できる |
| Feat3 | フラクタル描画: 結果表示部で深さ N まで再帰的に変換を適用して描画する |
| Feat4 | リアルタイム反映: 操作部の変更が結果表示部へ即時に反映される |
あとは Phase ごとに、同じく plan スキルを使って、F1 など個別の Foundation / Feature の計画を立てていきます。
例えば F1 の計画はこちら。
中の具体的な作業は、今回はあまりちゃんと読んでいません。(仕事じゃないし。)上の F1〜F4 の表の粒度くらいで違和感がなければ、計画に沿って F1 の実装を実行させます。
AI から受け入れ条件に従って人間側の確認依頼が来るので、動作確認をします。
OK なら、F2 の計画 → 実装、と進んでいきます。
細かい機能改善
例えば、フラクタルの Seed となる線を書く場合に、「正確に原点を端とする水平な線を引きたい」ように、正確な座標・角度で操作したいことがよくあります。
この程度の粒度のタスクであれば、plan スキルで計画を立てさせ、中身を軽く見て、これ以上分割しなくて良さそうであればすぐ実装に移らせます。
場合によっては plan せずに直接チャットで指示を出すこともありますが、うまくいかない場合、AI がだんだん自分が何をやっていたか混乱しだすことがあります。そのような場合も、一旦何をしたいのかを plan スキルを使って書き出しておくと、リカバリーが効きやすいです。
AI 自身に計画させてノールックでその計画を実行させても、チャットで直接実装指示をするより実装精度が高い気がしています。
AI 時代のリファクタ
さあ、機能改善を繰り返して使い勝手も増し、もともとネイティブ実行のみだったものが WebAssembly でも実行できるようになりました。
しかしながら、コードの中身はほとんどレビューせずに増改築を繰り返した結果、コードはぐちゃぐちゃです。
最初は「バイブコーディングで作って作り捨てだ!」くらいの気持ちで作り始めましたが、思いのほか良いものができてきたので、エンジニアとしてよりきれいなコードで残したい & 自分もコードの中身を理解しておきたくなってきます。
というわけでリファクタしましょう。
一般的にリファクタといえば、テストを書いて動作を保証してから、挙動を変えずに実装を整理するのが王道です。しかし、UI レベルでのテストを書くのはなかなか難しく、ユニットテストレベルでのテストだとユニット自体をがさっと全部作り替えるつもりなので使えません。
今回は仕事でもないですし、ドラスティックに作り直す方向にしました。
リファクタのフロー
これは私の設計哲学ですが、良い設計とは そのプロダクト(特にそのプロダクトが持つ情報)が、どのような抽象的な責務に分けられるかを理解した設計 です。
今回は、「プロダクトが現在持っている機能」という具体を洗い出し、そこから抽象的な責務に分解し、適する設計を抽象 → 具体方向に立てていくという流れでリファクタを行いました。
最初に機能を洗い出し、それらを計画に記述することによってリファクタ前後の品質をある程度担保しています。
機能洗い出しのような人間には辛い仕事も AI さんだとゴリッとやってくれるのが AI 時代だなって感じです。
plan スキルを使いながら立てた計画がこちら。
## 提案フロー(合意事項)
次の 4 段階で進める。
1. **機能の洗い出しと責務の整理**
現状のファイル配置は参照せず、アプリが「何を提供するか」だけを列挙し、ドメイン/責務単位にグルーピングする。
2. **デザインパターンの選定**
fractalium(Bevy + UI、ネイティブ/WASM)に合うパターン・境界の付け方を文書化する。実装詳細や具体的な型名はこの段階では詰めすぎない。
3. **モジュール分割と配置**
上記責務に対応するモジュール階層・データの流れ・依存の向きを決め、ディレクトリ構成と主要クレート内の配置方針を確定する。
4. **実装**
段階的に移行し、各段階でビルド・主要動作を検証する。
現状のファイル配置、つまり既存の責務設計を参照しないで機能を洗い出すところがミソです。
もともとがゴミコードなので。
そんな感じで、上記の Phase 1〜4 を順次計画しながらリファクタしていきます。
たしかこのあたりのフェーズからは、Claude Code ではなく Cursor を使っています。
コードの差分が追いやすいんですよね。Cursor。
そして、コードの中身もそれなりに真面目に読んで、可読性が高いか、責務がきれいに分かれているかを見ていきます。
問題発生
そして Phase 4: 実装まですべて終わったのですが、ここで問題発生。
計画の粒度が荒かったせいで、Phase 4 まで終わったのに、コード量の半分くらいが src 直下にある既存の古いごちゃまぜファイルに残ったままになりました。
AI としては、立てた計画の作業は完了しており、受け入れ条件もクリアされている、と言ってきます。
これは私のコミュニケーション不足で、src 直下のファイルは main.rs を除いて一掃し、すべてのコードを適切に責務分けされたレイヤーに移す、ということが計画に含まれていませんでした。
というわけで、Phase 5 で src 直下のファイルも全部移すことを伝えて、急遽、上記 Phase 1〜4 のようなことを繰り返すことに。
ある程度ここまでで責務のカテゴリ分けはできていたので、適切なカテゴリに落としていきます。
得られた知見
-
設計の粒度
-
もともとの計画では、機能の洗い出しとアーキテクチャ選択とディレクトリ設計までで終わっていました。ディレクトリだけではなく、
- ファイルとファイルごとの責務
- 公開メソッドのインターフェース設計
-
あたりまでやっておいたほうが良さそうかもしれません。
-
- 受け入れ条件
-
src 直下には main.rs しかない状態であるなど、どこまでやるのかが受け入れ条件として表現されているべきでした
-
おわりに
そんなこんなで、AI たちと仲良く Fractalium を作っていました。
途中から Cursor の Composer 2 という比較的安い AI を使っていましたが、わりと使えます。
が、それでも GW 中に集中して作っていたら、一週間くらいで月のサブスク分を使い果たしそうになってしまいました。
しかしながら、昔なんども作ろうと思い立って少し触っては挫折していたプロジェクトを、MVP なら数日、全体の詰めを含めても数週間でできてしまったのは、本当に AI 時代のありがたみだなあと思います。
ぜひ Fractalium で遊んでいってね。