こんにちは。エンジニアリンググループ GMの藤原聖です。このブログはマネジメントチームブログリレー 5日目の記事です。前回は窪田さんの、ローコードツールから Playwright 移行への道 2025 でした。
私はマルデバ・デジカル・デジスマという3つのチームのGMを担当していますが、その傍ら エムスリーテクノロジーズ株式会社 にてM3グループ各社のエンジニアリング支援も行っています。 最近はその支援の一環で、とあるAndroidアプリのリファクタリングをClaude Codeを使って高速に進めています。

本記事では、AndroidアプリのリファクタリングでClaude Codeを利用する際のTipsをいくつか紹介できればと思います。
背景
リファクタリング前の状況
対象となるAndroidアプリは、10年以上前に書かれたコードベースをほぼそのままに、機能追加やAndroid OSのアップデート対応を行ってきました。 具体的な状況は以下のとおりです。
- 規模: 30〜40個程度のActivityを持つサーバークライアント型モデル
- 機能: NFCやQRコードリーダーなどのハードウェア連携機能を含む
- 言語: 全てJava
- ライブラリ: ほぼ未使用。通信、非同期処理はAndroid SDK標準クラスを利用、DB保存は自前実装
- アーキテクチャ・DI : なし (Activity/Fragmentにロジック含め記述)
- テストコード : なし
ログイン処理や複数サーバーへの接続など複雑な要件もあり、そこそこの規模感です。 大きな課題として、以下の2つが挙げられます。
AsyncTaskなど非推奨APIへの依存
AsyncTaskやstartActivityForResultなど、すでに非推奨となったAPIがいくつか使われており、将来的なOSのアップデートでアプリが動かなくなる可能性があります。
Activityの肥大化、シングルトンの神クラス
Activityにビジネスロジックが記述され、数千行にもなったActivityがいくつか存在していました。また、通信やDB保存など、アプリ共通の処理についてはシングルトンの神クラス(God Class)が担っており、こちらも数千行を超えてコードの見通しが悪くなっていました。
リファクタリング方針
今後の事業成長とユーザー体験の向上を見据えた時、これらの課題を解決し、開発生産性を向上させるための大規模なリファクタリングが必要だと判断しました。
アーキテクチャやライブラリ選定は一般的な構成に寄せることにしました。 マルチデバイスチームの渡辺さんが書いた以下の記事にあるアーキテクチャやライブラリを採用しています。
その他のリファクタリング方針は以下のとおりです。
- Kotlinへの完全移行: 新規コードは全てKotlinで記述。
- Jetpack Compose/Navigationの利用は一旦見送り、全体のリファクタリング完了後の採用とする。
- XMLレイアウトでも要件的には問題なし。リファクタリング後のUI/UX改善のフェーズがあればそれと一緒に行う予定。
Claude Codeを使った高速リファクタリング
では実際にリファクタリングを進めていて、役立ったTipsを紹介します。 なお、Claude Codeのバージョンは2.0.50、モデルはSonnet 4.5を利用しています。
まずは分析ドキュメントの整備
実際にコードを書かせるより先に、まずはドキュメントの整備から始めました。生成した計画は /RefactoringDocs というフォルダを作り、保存しています。 最初は「リファクタリング計画を作って」とリクエストしてドキュメントを作りました。 以下のような項目が並び、一見悪くはないものの、「人間が考えるのと大差ないな」という結果になりました。
- AsyncTask → Kotlin Coroutines への移行
- カスタム通信クラス → Retrofit + OkHttp への移行
- MVVMアーキテクチャへの段階的移行
- Dependency Injection (Hilt) の導入
リファクタリング計画を立てる上でより有用だったのは、画面遷移やActivityごとの機能分析でした。 具体的には以下のようなプロンプトを入力しています。
「Activityの画面遷移を分析し、機能ごとにいくつかのかたまりに分けてください。またその際、その中のクラス(ActivityやFragment等)の行数やコードの複雑さを分析してください。結果はマークダウン形式でファイルに出力してください。」
これで、Activityがいくつかの機能ごとのまとまりに分類され、それぞれの複雑度が出力されました。 以後は上記のレポートを元に、以下のように追加で分析してもらいました。
「画面遷移分析レポート.mdのファイル内にある、ログインに分類されているActivityについて、どこから遷移してどこに遷移するのか、遷移を分析してまとめてください。結果はこれまでと同様にファイルに出力してください。」
これによってより詳細な画面遷移、Activityごとの機能分析を得ることができ、どの画面からリファクタリングするかの計画が立てやすくなりました。
アーキテクチャの確定とDIツールの導入
当初リファクタリング計画を立てた際は、「カスタム通信クラス → Retrofit + OkHttp への移行」など機能単位のリファクタリングを提案されました。 最初はその通りに機能ごとに必要なライブラリを入れリファクタリングを進めていたのですが、進めるに従って、機能ごとではなく画面ごとにリファクタリングをした方が効率が良いと考えるようになりました。 またそのために、アーキテクチャを確定させ、DIツール(Hilt)を早く導入した方が良いという結論になりました。 その理由は後述するTipsに関わってくるのですが、主に以下の2点です。
- アーキテクチャを確立し、ViewModelより上のUI層のリファクタリングを完成させたほうがコードの見通しが良くなる。
- 古いコードの神クラスをUI層から追い出し、Repository層以下に閉じ込め、最後にリファクタリングすることにした。
- UI層のリファクタリングは見本を示せば、Claude Codeが良い感じにやってくれる。
DIツールを導入し、アーキテクチャを確定させたら、以下のように理想とするアーキテクチャ図を CLAUDE.md に記載しました。ちなみにこのドキュメントも以下のように入力し、Claude Codeに出力させています。
「domain/feature/infra のパッケージにあるクラスがリファクタリング後のアーキテクチャです。このアーキテクチャを説明してください。」
#### 階層型アーキテクチャのパッケージ配置
jp.co.company.app/
├── feature/ # UI層(画面単位でパッケージ分割)
│ ├── login/
│ │ ├── view/ # Activity, Fragment, Dialog
│ │ └── viewmodel/ # ViewModel
│ ├── user/
│ │ ├── view/
│ │ └── viewmodel/
...
├── domain/ # ドメイン層(ビジネスロジック)
│ ├── data/ # データモデル(sealed class, data class)
│ └── repository/ # Repositoryインターフェース
├── infra/ # インフラ層(実装)
│ ├── repository/ # Repository実装
│ ├── api/ # Retrofit API定義
│ ├── datasource/ # データソース
│ └── di/ # Hilt DIモジュール
└── util/ # ユーティリティ
├── legacy/ # レガシーブリッジ
└── extensions/ # 拡張関数
と、ここまで書いても実装中は以下のようなアーキテクチャ違反をよくしてくるので、コードレビューの際は細かく修正依頼を出していました。
- Activityがビジネスロジックの状態を持ってしまう(ViewModelに持たせるのが正しい)
- ActivityがDomain層やRepository層のクラスを直接参照している
神クラスはUI層から遠いところに
先述したように、リファクタリング前のコードにはシングルトンの神クラスがいくつか存在します。 これらのクラスはアーキテクチャのRepository層以下に閉じ込め、後からリファクタリングすることにしました。 Repository分類の最適化も最初は行わず、あえて多くのRepositoryを作成させます。そして、似たような機能を持つ画面のリファクタリングを終えたのち、以下のようにして再配置を行いました。
「XXRepositoryをYYRepositoryに統合してください」 「XXRepositoryのhogefugaという関数はZZRepositoryに移動してください」
再配置はAIにとってはお手の物なので、Repositoryに注入されるクラスも最適化されていき、様々な機能を持つ神クラスを機能ごとに分類でき、機能ごとにリファクタリングができるようになりました。
元のコードは残す
リファクタリングの途中では、元のコードは極力手をつけずに残すべきです。 AIは元の実装と微妙に違った実装をしてくる場合もあり、人やAIが後ほど見返すことが多いためです。 モジュールやパッケージを分けるか、共通のプレフィックスをつけるなどして、リファクタリング前後のコードの区別を分かりやすくする必要があります。
不要なコードの削除
元のコードは残した方が良いですが、明らかに不要なコードであれば極力削除した方が良いです。 特に不要なフラグなどが残っていた場合、AIは細かい仕様までは追いきれない時もあるため、リファクタリング後も不要なコードを出力してきてしまいます。
使うライブラリを明確に
AsyncTaskをKotlin Coroutinesに移行するなど、大きなライブラリの変更については、計画に従ってくれるため大きくルール違反をすることはありません。 ただ、元のコードを基本的には引き継ごうとするので、非推奨なAPIへのアクセスが残っていたり、理想的なコードにはなっていないことが多いです。 以下のリストは、1回目の指示では左側のコード(古い実装)を出力しがちだったため、再度の作り直し指示が必要だったものです。
- LiveData vs Kotlin Coroutines Flow
- startActivityForResult vs ActivityResultLauncher
- AlertDialog vs DialogFragment
理想的なActivityを作る
ここまでの流れで、リファクタリング後のアーキテクチャやライブラリが決まったら、中規模程度のActivityをリファクタリングして、理想的な状態に持っていきます。 1つ理想的な「Activity - ViewModel - Repository」の実装セットができたら、以後はそれを参考にしてもらいます。
「XXActivity.ktを参考に、YYActivity.javaをリファクタリングして、feature.zz.view.ZZActivity.ktを出力してください。」
と指示することで、80点〜90点ぐらいのコードが一発で出力されるようになりました。
一気に書きすぎない
巨大なActivityをリファクタリングしていると、(Claude Codeの)Contextがあふれるせいか、細かい実装を見逃したりします。 実際Claude Codeも「ステップを分けて出力します」などと言ってくるので、その際はその提案に従いましょう。まずはアーキテクチャの基礎となるところを作成してもらい、未実装の部分についてはTODOコメントで残してもらった状態で一度コミットした方が良いです。 その後、Contextを引き継がない状況でも、 「ZZActivityのリファクタリングの続きをしてください」 と何度かやって残りを実装していくことができます。
最後にもう一度確認
ここまでの手順を踏めば、各Activityのリファクタリングが本当に高速に進んでいきます。しかしながらアプリを実際に動かして確認すると、どうしても、細かい実装が異なったりする部分が見つかります。 その場合は 「ZZActivity.ktの動きが正しいか、もう一度よく実装を比較して、元の実装と同じようになるようにしてください。」 と指示を出すことで、細かな差分を見つけて修正してくれます。
最後に
おおよそ1ヶ月ぐらいで全画面の1/3ぐらいのリファクタリングが終わりました。 Repository層からの神クラスの引き剥がしや、大きなActivityのリファクタリングが残っているため、単純計算であと2ヶ月で完了するわけではありませんが、人間が全てのコードを書いていた時に比べると驚異的なスピードで進んでいます。 また、私個人はここ数年、仕事上はコーディングから離れていたのですが、Claude CodeをはじめとしたAIエージェントが知識不足を補ってくれたり、面倒な繰り返しコードを記述してくれるため、コーディングの楽しい部分だけを享受しながらリファクタリングを進めることができます。とても良い時代になったと感じています。
We are hiring!!
エムスリーはiOS/Android/Flutterによるアプリ開発に興味のあるエンジニアを大募集しています。
ご興味のある方は、下記のサイトから是非カジュアル面談等、お申し込みください!
本記事のテーマであるエムスリーテクノロジーズ株式会社でもアプリエンジニアを大絶賛募集しています!エムスリーグループ各社を技術の力で加速し、新たな価値を創造する仕事に少しでも興味を持ちましたら、ぜひカジュアル面談などでお話しましょう!