
どうも、YOUTRUSTでアプリ開発のテックリードをしている朝日です。
以前、ボードゲームを一人でやっていると書いたのを覚えていらっしゃる方はいますでしょうか。 いたらさすがに連絡ください。 最近は、なんと5歳の息子がそれを見かねたのか一緒に遊んでくれるようになってとても幸せな毎日を過ごしています。 YouTubeや仮面ライダーの様なデジタル世界から強制的に離れられるのでとても良いなと手前味噌ながら思っていたりします。
さて、今回はYOUTRUSTのFlutterアプリ開発でClaude Codeをどう活用しているか、特に レイヤー別ガイダンスシステム について書いていきます。「AIにコードを書かせてみたけど、プロジェクトの規約に沿わない出力ばかりで結局手直しが大変…」という経験がある方にはきっと参考になるはずです。
そもそも何を作ったのか
一言でいうと、チームの設計思想・コーディング規約をAIが読める形で体系化した というものです。
具体的には、.ai/guidance/ というディレクトリに各アーキテクチャレイヤーのガイダンスドキュメントを整備しました。
.ai/guidance/
├── screen/ … 7ドキュメント
├── page/ … 6ドキュメント
├── component/ … 5ドキュメント
├── view_model/ … 9ドキュメント
├── facade/ … 10ドキュメント
├── store/ … 7ドキュメント
├── api_client/ … 6ドキュメント
├── log_request/ … 9ドキュメント
├── unit_test/ … 4ドキュメント
└── localization/ … 3ドキュメント
計 66ドキュメント(+ ルート直下に5ファイル = 全71ファイル)
これらのガイダンスをClaude Codeが読み込むことで、プロジェクトの規約に沿ったコード生成やレビューが可能になっています。
なぜ「レイヤー別」なのか
YOUTRUSTのFlutterアプリは、Screen → Page → Component → ViewModel → Facade → Store → API Client というレイヤードアーキテクチャを採用しています(この構成の詳細は 過去の記事 で書きました)。
各レイヤーには、それぞれ固有の設計判断やパターンがあります。たとえば:
- Screen層:
HookConsumerWidgetを継承し、buildメソッド内の記述順序(VM宣言 → Hooks → テーマ変数 → return)が決まっている - ViewModel層:
ViewModelMixin<ViewPhase>+ExceptionScopeOwnerを必ず適用し、3種類のVMパターン(Standard / Parameterized / StateKey)から選択する - Facade層: ビジネスロジックの協調パターンが複数あり、戻り値のルールやエラーハンドリング戦略が細かく定義されている
これらを1つの巨大なルールファイルに詰め込んでも、AIは「今どのレイヤーの話をしているのか」がぼやけてしまいます。レイヤーごとにドキュメントを分離し、必要なときに必要なガイダンスだけを読み込む設計にしたことで、AIの出力精度が格段に上がりました。
ガイダンスドキュメントの中身
各レイヤーのディレクトリには、以下のような構成でドキュメントが並んでいます。ViewModel層を例にすると:
| ファイル | 内容 |
|---|---|
overview.md |
クラス定義と責務 |
playbook.md |
Base Structure・状態遷移パターン |
lifecycle.md |
ライフサイクルメソッドの詳細 |
view_phase.md |
ViewPhaseによる状態管理 |
error_handling.md |
例外スコープと非同期エラー伝播 |
file_organization.md |
ファイル配置・命名規則 |
best_practices.md |
実装原則とアンチパターン |
migration_guide.md |
レガシーパターンからの移行ガイド |
ポイントは、overview.md だけ読めば「そのレイヤーが何者か」がわかり、playbook.md を読めば「具体的にどう書くか」がわかるという構成です。AIに限らず、人間が読んでも理解しやすい粒度を意識しています(というか、もともと人間のために書き始めたものが、AIにも効くことがわかった、という順序です)。
抽象論ではなく具体的なコードを書く
各ドキュメントには実際のプロダクトコードに近いサンプルを載せています。たとえばViewModel層の playbook.md には、こんな感じで標準パターンのコード全体が書かれています:
@riverpod class UserListViewModel extends _$UserListViewModel with ViewModelMixin<UserListViewPhase>, ExceptionScopeOwner implements AsyncEffectIgnition { @override UserListViewPhase build() { ref.onDispose(exceptionScope.close); return const UserListViewPhase(); } // ... }
「ViewModelMixin を使ってください」という一文だけでは、AIは独自の解釈で書き始めてしまいます。「こう書いてほしい」を具体的なコードで示すのが、AIの出力精度を上げる上で一番効きました。
これは人間にとっても同じで、「コードのお手本」があると「なるほど、こう書けばいいのか」とすぐに真似できます。
migration_guide.md で過去のコードも正直に書く
個人的に気に入っているのが migration_guide.md の存在です。レガシーパターンが存在する事実を隠さず、新パターンへの移行パスを明示しています。
これが大事な理由は2つあって、1つはAIがレガシーコードを参照して古いパターンで書いてしまうのを防げること。もう1つは、新メンバーが既存コードを読んだとき「あれ、ガイダンスと違うコードがあるぞ?」と混乱しなくて済むことです。
「イケてない過去のコードがある」ことを正直にドキュメントに書くほうが、新メンバーにとっても、AIにとっても親切だと思っています。
AIの出力がどう変わったか

ガイダンスを整備する前と後で、Claude Codeの出力がどう変わったか。
Before: ガイダンスなし
// AIが書いたViewModel(ガイダンスなし) @riverpod class UserListViewModel extends _$UserListViewModel { @override AsyncValue<List<User>> build() async { return await fetchUsers(); } }
一見動きそうですが、YOUTRUSTのアーキテクチャとは全く違います。ViewModelMixin も ExceptionScopeOwner も ViewPhase も使われていない。Riverpodの一般的な使い方としては間違っていないのですが、プロジェクトの規約には沿っていない わけです。
After: ガイダンスあり
// AIが書いたViewModel(ガイダンスあり) @riverpod class UserListViewModel extends _$UserListViewModel with ViewModelMixin<UserListViewPhase>, ExceptionScopeOwner implements AsyncEffectIgnition { @override UserListViewPhase build() { ref.onDispose(exceptionScope.close); return const UserListViewPhase(); } @override Future<void> launch() async { await exceptionScope.run(() async { state = _transitions.preparing(state); await UserFacade.fetchUsers(read: ref.read); state = _transitions.idle(state); }); } }
ViewModelMixin, ExceptionScopeOwner, AsyncEffectIgnition, ViewPhase による状態遷移、exceptionScope による例外ハンドリング。全部プロジェクトの規約通りです。「手直しがほぼ要らないコード」をAIが最初から出してくれるようになりました。
この差を生んでいるのが、71ファイルのガイダンスです。
思わぬ副産物:最高のオンボーディング
ここまではAI活用の話でしたが、実は .ai/guidance/ が一番威力を発揮したのは 新メンバーのオンボーディング でした。
新しくチームに入ったメンバーにまず .ai/guidance/ を一通り読んでもらうようにしています。各レイヤーの overview.md → playbook.md と読み進めるだけで、アーキテクチャの全体像から具体的な実装パターンまでキャッチアップできます。
従来のオンボーディングは「既存コードを読んで雰囲気を掴む」「PRレビューで都度指摘して覚えてもらう」というスタイルでしたが、正直これだと暗黙知の伝達に時間がかかるし、レビュアーの指摘内容もバラつきます。ガイダンスがあることで、「なぜこう書くのか」の意思決定プロセスが文書化されているので、新メンバーが設計の背景まで理解した上でコードを書き始められるようになりました。
「足りないところを書いてもらう」という最強のタスク
さらに効果的だったのが、ガイダンスの不足部分を新メンバーに追加してもらう というタスクです。
新メンバーはプロジェクトのコードを読みながら「ここのパターンはガイダンスに書かれていないな」という箇所を見つけます。それをドキュメントとして追加してもらうことで、以下の効果が同時に得られます:
- コードリーディングの動機づけ: 漫然と読むのではなく「ガイダンスに不足がないか」という観点で読むので、理解が深まる
- アウトプットベースの学習: 読んだ内容を自分の言葉でドキュメントにまとめることで定着する
- チームへの早期貢献: 入って間もないメンバーでも「ガイダンスを充実させた」という成果が残る
- ガイダンスの継続的改善: ベテランが「当たり前」と思っている暗黙知が言語化される
「コードを読め」ではなく「ガイダンスを読め、足りないところを書け」にしたことで、オンボーディングの質が劇的に上がりました。そしてそのガイダンスがAIの判断基準にもなるので、チームの知識が蓄積されればされるほどAIの出力精度も上がっていくという好循環が生まれています。
ガイダンスの育て方
71ファイルと聞くと「最初からそんなに書いたの?」と思われるかもしれませんが、一気に作ったわけではありません。
最初はViewModel層の overview.md と playbook.md だけでした。PRレビューで「このパターンはガイダンスにしたほうがいいな」と感じたものを都度追加していった結果、今の規模になっています。
育て方のコツとして意識しているのは以下の3つです:
- PRレビューの指摘をガイダンスに変換する: 同じ指摘を2回したら、それはガイダンスにすべきサイン
- 抽象的な原則だけでなく具体的なコードを書く: 「単一責任にしましょう」ではなく「
buildメソッド内の順序はこう」と書く migration_guide.mdを恐れない: レガシーパターンが存在する事実を隠さず、新パターンへの移行パスを明示する
ガイダンスもGit管理でPRレビューの対象にする
ガイダンスがコードベースの中にある(.ai/guidance/ としてGit管理されている)ので、ルール自体がPRレビューの対象になる というのも大きいです。
ガイダンスの変更は全員が見られるし、「この規約はもう古いのでは?」という議論もPR上でオープンにできます。Notionやコンフルエンスに書くのではなくコードベースに置いたのは、この「変更の透明性」と「AIが直接参照できる」という2つの理由からです。
この仕組みがもたらしたもの
導入して最も変わったのは、人間のレビューで見つかる指摘の質 です。
以前は「ここの順序が違います」「この命名規則に従ってください」といった機械的な指摘がレビューの大部分を占めていました。今はそういったチェックはAIが事前に潰してくれるので、人間のレビューは「この設計判断でいいのか」「このユーザー体験は意図通りか」といった、より本質的な議論に集中できるようになっています。
まとめると、.ai/guidance/ がもたらしたのは3つの効果です:
- AIの出力品質向上: プロジェクト規約に沿ったコードが最初から出てくる
- オンボーディングの加速: 新メンバーがガイダンスを読む → 不足を埋めるサイクルで、学習と貢献が同時に回る
- レビューの質的変化: 機械的チェックをAIに任せ、人間は本質的な議論に集中できる
AIの出力を「プロジェクトの規約に沿わせる」ための最も確実な方法は、プロジェクトの規約をAIが読める形で書き下すことでした。そしてそれは同時に、人間にとっても最高のドキュメントになるということが、一番の学びだったように思います。
最後に
YOUTRUSTでは一緒に働く仲間を募集しています! アプリエンジニアはもちろん、Webエンジニアも絶賛募集中です。
弊社に興味ある人はもちろん、ない人もぜひ一度お話ししましょう。