Claude CodeにFlutterアプリのリファクタをお願いしてみよう!
はじめに
弊社が提供しているモバイルアプリのプロジェクトは、ネイティブからFlutterに移行した背景もありディレクトリ構造がちょっとカオスです>< また、複数の責務が混在しているファイルもある状態です。
以下が現在のディレクトリ構造(抜粋)です。
lib/
├── data/ # データレイヤー
│ ├── data_sources/
│ ├── entities/
│ └── providers/
├── ui/ # UIレイヤー
│ ├── features/
│ │ ├── hoge_a/
│ │ └── hoge_b/
│ └── components/
├── hoge_c/ # 機能別ディレクトリ
│ ├── presentation/
│ └── ui/
└── hoge_d/
├── presentation/
└── ui/
上記の通り、機能単位の構造とレイヤー単位の構造が混在しています。
そのため、現在は少しずつレイヤー単位のディレクトリ構造に寄せ、リファクタを進めています。
手作業でもリファクタリングできますが、地味で大変です。そこでコーディング禁止?! AI Agent Dev Day(社内イベント)を開催しました!をきっかけにClaude Codeを活用してリファクタリングを進めてみました🎉
Webアプリチームがすでに実施していたGitHub Copilotと進めるWEBリアーキテクチャでの知見を活かし、ペアプロのような形式で実施しました。
手順
(Claude Codeが導入されていることを前提に)
- Claude CodeのCustom Commandファイルを作成(Custom Commandについての説明は省略します)
- プロンプトを練り練り...
- Custom Commandを実行!
作成したCustom Command
不完全なプロンプトではありますが、現状は以下の内容を投げるとよしなにリファクタを進めてもらえるようになりました。
実行内容は主に二つです。
- リファクタリングしたいディレクトリの構造を新しいディレクトリ構造に変更する
- 責務分離されていないものはリファクタしてもらう
$TARGET_DIRはCustom Command実行時に渡せる変数です。実行時に$TARGET_DIRへリファクタリングしたいディレクトリのパス(ex: lib/hoge_detail)を渡してあげることで汎用性のあるものにすることができました。
$TARGET_DIR の現状のディレクトリ構成を確認してください。
【新しい構成にする手順】
1. $TARGET_DIR 内で、一つのファイルに異なる責務(例:freezedアノテーションのついたState(Model)とProvider/Notifier)が混在している場合は、必ず責務ごとにファイルを分割してください。
- Model(stateの場合もある)はentities/[feature_name]/配下の[feature_name].dartとして分離し、freezedで生成しなおしてください。
- ProviderやNotifierはproviders/[feature_name]/[feature_name]_provider.dartにまとめてください。
- クラス名や型名もViewModelやViewModelImplではなく、ProviderやNotifierの命名規則に合わせてリネームしてください。
- それぞれのファイルでimportパスやpartファイルの指定も正しく修正してください。
2. $TARGET_DIR を以下のツリー構成に変更してください。
lib/
├── data/
│ ├── providers/
│ │ └── [feature_name]/
│ │ └── [feature_name]_provider.dart
│ │ └── ...
│ ├── entities/
│ │ └── [feature_name]/
│ │ └── [feature_name].dart
│ │ └── [feature_name].freezed.dart
│ │ └── [feature_name].g.dart
│ └── repositories/
│ └── [feature_name]_repository.dart
└── ui/
└── features/
└── [feature_name]/
├── children/
│ └── [feature_name]_hoge.dart // hogeは任意の子要素名
└── [feature_name]_page.dart
当てはまるディレクトリが上記にない場合は、調べてください。
3. これらのファイル間のimportパスを新しいパス・ファイル名・型名に合わせて修正してください。
4. $TARGET_DIR ディレクトリ自体は不要になるため削除してください。
5. `flutter analyze --no-fatal-infos --no-fatal-warnings` を実行して、コマンドが正常終了することを確認してください
## 注意事項
- feature_nameは、元のファイル名をもとにしてください。
- クラス名や型名もViewModelやViewModelImplではなく、ProviderやNotifierの命名規則に合わせてリネームしてください。
- Model(stateの場合もある、freezedアノテーション付き)は必ずentities配下に、ProviderやNotifierはproviders配下に分離してください。
- view_modelとview_model_implは、**同じ責務・役割のもののみ** providerファイルにまとめてください。異なる責務や複数のmodel/providerがある場合は、**必ず1ファイル1クラス(provider/model/repository)となるように分割してください**。
- 一つのファイルには一つのproviderやmodel,repositoryを定義してください。
- importパスも新しいファイル名・クラス名に合わせて修正してください。
- ファイル内部のロジックや実装内容は絶対に変更しないでください。
- flutter analyzeは手順5まで実行しないでください。
- **他のファイルやディレクトリ、ファイル内部のロジックや実装内容は絶対に変更しないでください。**
リファクタ汎用プロンプトができるまで
理想通りにリファクタしてもらうまでに、何度もプロンプトを修正しています。
修正のポイントは、
- 大事なことは太字にする、何度も書く。
- プロンプトの指示は具体的にする。
- プロンプトが大きくなってくると後半の内容は忘れられがちなため、手順として切り出すか、手順の中に注意事項として明記する。
- ディレクトリ構造の指示について、ファイル名に
~Itemなど決めうちにしてしまうものを入れると全部~Itemに寄せてしまうので、hoge // hogeは任意の子要素名のようにしてあげる。よしなにしてくれます。 - Custom Commandの実行中にClaude Codeが不可解な動きを始めたら停止し、追加の指示を出すか、Custom Commandのプロンプトを修正して再度カスタムコマンドを実行する。
- このとき、必要に応じて/clearを使用して過去の内容を忘れてもらいます。
-
flutter analyze --no-fatal-infos --no-fatal-warnings(infoやwarningレベルでは処理を止めず、エラーのみを検知するため)でエラーが残っていないか確認させるようにする。癖付いて追加の指示を出したあとの修正のタイミングでも実行してくれます。
おわりに
汎用プロンプトですぐにリファクタが完了するのでとっても素敵です。これで気軽にリファクタができます🎉
また、Claude Codeの作業内容はClaude Codeにコミットしてもらうと、詳細に変更内容を記載してくれるのですごく便利でした。
git worktreeでの並行作業にもチャレンジしてみましたが、UIの動作確認などが必要な場合はあまり向いていないことが分かりました😌
これからもClaude CodeなどのAIを活用したエンジニアリングに取り組んでいきます🤝
最後までお読みいただき、ありがとうございました。
Discussion