LASSIC Media らしくメディア
アプリ状態管理リファクタリングを外注する進め方
LASSIC IT事業部|元請(プライムベンダー)としてシステム保守・運用を受託
この記事のポイント
- 状態管理リファクタリングは「全面刷新」ではなく機能単位の段階移行がリスクを抑えます
- Flutter・iOS・Androidそれぞれに主流設計パターンがあり、選択基準を知ることが外注成功の前提です
- 外注時は設計方針の確認・段階レビュー・テストカバレッジ基準の明文化が品質を左右します
目次
状態管理リファクタリングとは何か
モバイルアプリの状態管理リファクタリングとは、UIの表示状態・非同期処理の結果・ユーザー入力などを一元的に管理するコードの設計を、保守・テスト・拡張しやすい形に整え直す取り組みを指します。機能追加の繰り返しで肥大化したコードを「全面刷新」するのではなく、既存アプリを動かしながら内部品質を高めていく点が特徴です。
状態管理の設計が整っていると、UI部品(ウィジェット・View)がデータの取得・加工に直接関与せずに済みます。単一情報源(SSOT: Single Source of Truth)として状態を一箇所で管理し、副作用(APIコール・ローカルDB書き込みなど)を分離することで、テストが書きやすくなり、バグの原因特定も容易になります。
肥大化・密結合が引き起こす具体的なリスク
状態管理の設計が崩れたまま機能追加を続けると、主に次の3つの問題が深刻化します。いずれも外注工数や品質に直結するため、早期に整理する意義があります。
バグの増加と原因特定コストの上昇
UI層が直接APIを呼び、その結果を別のUIコンポーネントが読み取るような密結合な実装では、どこかで状態が書き換わると予期しない画面が更新されます。バグを1件修正するために影響範囲を洗い出すだけで大きな工数が発生します。
テストが書きにくい設計では、リグレッション(修正に伴う別バグの発生)の発見も遅れます。リリースサイクルが短い現代のアプリ開発では、この遅れが競争力に影響します。
新機能追加の速度低下
状態管理の設計が整っていないと、新機能を追加するたびに既存コードの読解と影響調査が必要になります。チームメンバーが増えても生産性が上がりにくく、オンボーディングにも時間がかかります。
コードの見通しが悪いまま開発が続くと、担当者以外が触れなくなる「属人化」も進みます。担当者の離職や異動が直接的なプロジェクトリスクになります。
外注時の仕様共有困難
外注パートナーにコードを引き渡す場合、状態管理の設計が明文化されていないと仕様の共有に余分なコストがかかります。レビューの基準を設定しにくく、成果物の品質を確認しづらい状況が生まれます。
設計を先に整理しておくことで、外注パートナーとの役割分担が明確になり、仕様変更や追加要件の影響範囲を見積もりやすくなります。
プラットフォーム別・主流設計パターンの比較
状態管理のリファクタリングを外注する場合、利用するプラットフォームとフレームワークに適した設計パターンを採用することが前提です。外注パートナーへの技術要件として明示できるよう、主流のパターンを整理します。
| プラットフォーム | パターン名 | 主な特徴 | 向いているケース |
|---|---|---|---|
| Flutter | Riverpod | コンパイル時に型エラーを検出・BuildContext非依存・テスト容易性が高い*1 | 新規設計・MVVM疎結合で拡張性を重視する場合 |
| Flutter | BLoC | StreamベースでイベントとStateを分離・Flutter Favoriteパッケージ*2 | 複雑なフロー・大規模チーム開発 |
| Flutter | Provider | InheritedWidgetラッパー・学習コスト低め | 小規模・既存Providerコードの段階移行中 |
| iOS (Swift) | Observation + MVVM | iOS 17以降の@Observableマクロで宣言的な変更検知・SwiftUI統合 | Swift Concurrency(async/await)を使う現代的なiOSアプリ |
| iOS (Swift) | Combine + MVVM | リアクティブストリームによる状態伝播・iOS 13以降 | iOS 13〜16をサポート範囲に含む場合 |
| Android (Kotlin) | ViewModel + StateFlow | Android公式推奨・コールドフロー管理とUI層分離*3 | Jetpack標準スタックに合わせた設計 |
Riverpod:コンパイル時の型エラー検出とテスト容易性が高い選択肢
Flutter向けRiverpodは、BuildContextに依存せず状態を宣言できます。コンパイル時に型エラーを検出できるため、実行時の予期しないクラッシュを防ぎやすいです。状態をUIから独立して管理できるため、ウィジェットの再描画範囲を最小化しやすく、パフォーマンス改善にも寄与します。
テスト面では、外部依存(APIクライアント・ローカルDBなど)をDI(依存性注入)で差し替えられます。UIを起動せずにロジックのユニットテストを書ける点は、大規模なリファクタリング時に品質を保ちながら移行を進める際に有効です。
BLoC:イベント駆動で複雑フローを明示化
BLoC(Business Logic Component、ビジネスロジックをUIから分離するアーキテクチャパターン)は、ユーザー操作を「イベント」として受け取り、「状態」を返すStreamベースの設計です。フローが複雑なアプリや、複数人が同じ機能を並行開発する場合に、責務の境界を明確にしやすいです。
pub.devでFlutter Favoriteバッジを取得しており*2、月間ダウンロード数は183万回以上です。エコシステムが成熟しており、採用実績の多いパートナーを選びやすい利点があります。
iOS・Android:公式スタックに沿った設計が保守性を高める
iOSではSwift 5.9以降で利用可能な@Observableマクロ(Observationフレームワーク)がSwiftUIと連携しやすく、iOS 13〜16をサポートするアプリではCombineフレームワークが選択肢に挙がります。AndroidではJetpack ViewModel + StateFlowがAndroid公式推奨の組み合わせです*3。
外注パートナーへの要件定義では、対応するOSバージョン・Xcode/AGPバージョン・既存コードの設計パターンを明記しておくことで、見積もりのブレを減らせます。
段階移行アプローチ — 機能単位で進める4ステップ
状態管理のリファクタリングは、一度に全画面を書き直す全面移行よりも、機能単位で段階的に進める方がリスクを抑えられます。プロジェクトを止めずに品質を改善できる進め方を4ステップで整理します。
ステップ1:現状の設計マッピングと優先度設定
まず既存コードの依存関係を可視化し、どの画面・モジュールが特に密結合か、バグが頻発しているかを洗い出します。この工程では、コードを変更せずに設計を「読む」作業が中心です。
外注する場合は、この設計マッピングを外注パートナーと共同で行うケースと、社内で先行して実施してから依頼するケースがあります。社内で先行する場合、パートナーへの引き渡し精度が上がり、見積もりの齟齬が起きにくくなります。
ステップ2:最もシンプルな機能から移行開始
優先度の高い画面の中でも、依存が少なく動作確認が容易な機能から移行します。「最初に複雑な機能から手をつける」と移行中に大量のバグが発生しやすいため、シンプルな機能で設計パターンと移行手順を確立してから横展開します。
この段階でテストカバレッジの基準(例:新規追加コードに対するユニットテストカバレッジ70%以上)を設定し、外注パートナーと合意しておくことが大切です。後から基準を変更すると追加工数が発生します。
ステップ3:機能単位でレビューと本番適用
1機能分の移行が完了したらコードレビューと動作確認を行い、本番に適用します。機能ごとにリリースサイクルを回すことで、問題の局所化と早期発見が期待できます。
外注パートナーへのレビュー体制についても事前に合意します。社内エンジニアがレビューを担う場合は、Flutterや対象プラットフォームの技術的な読解力が必要です。社内に適切なレビュアーがいない場合は、外注パートナー内にシニアエンジニアを1名置くよう契約で求める方法もあります。
ステップ4:フォルダ構造・命名規則の標準化
移行が一通り完了したら、フォルダ構造と命名規則を整理します。Flutterであれば機能単位(features/)かレイヤー単位(lib/data/, lib/domain/, lib/presentation/)でフォルダを切り、状態管理クラス・リポジトリ・モデルの置き場所を明文化します。
この標準化ドキュメントを外注先との共通認識として持っておくと、次の機能開発やさらなる改修の依頼時にオンボーディングコストが下がります。
内製 vs 外注の判断基準
状態管理のリファクタリングを内製で進めるか外注するかは、チームのスキルセット・工数・リスク許容度によって変わります。判断の目安を整理します。
内製が現実的なケース
対象アプリのコードベースを熟知したモバイルエンジニアが在籍しており、リファクタリング期間中も日常の機能開発と並行して対応できる工数が確保できる場合は、内製が選択肢になります。
ただし、設計パターンへの精通度が低い状態で進めると、「リファクタリングしたつもりが別の密結合を生んでしまった」という状況が発生します。外注の検討理由としてよく挙がるのは、社内エンジニアが実装はできるが設計レビューができないケースです。
外注が有効なケース
次のようなケースでは、外注による専門知見の活用が有効です。
- 社内にFlutter/iOS/Androidの状態管理設計に詳しいエンジニアがいない
- リファクタリングと並行して新機能開発を止められない
- 設計改善の「完成状態」のイメージが社内で共有されていない
- 担当エンジニアが離職・異動予定で引き継ぎを兼ねた整理が必要
外注の場合は設計方針の最終判断を社内に残すことが重要です。「外注先が設計をすべて決める」形では、完了後に社内がコードを保守できない状態が生まれます。発注側が設計要件を提示し、外注パートナーが具体的な実装方針を提案するという役割分担が成果につながります。
内製・外注を問わず必要なスキルと工数の目安
内製でリファクタリングを進める場合、対象とするプラットフォームのアーキテクチャパターン(MVVM・Riverpod・BLoCなど)への実務経験、テスト実装の経験(ユニットテスト・ウィジェットテスト)、コードレビューを担えるシニア相当のエンジニアが最低1名必要です。
工数はアプリの規模と密結合の深度によって大きく変わりますが、中規模アプリ(画面数30〜50程度)での段階移行は、設計マッピングからフォルダ構造標準化まで含めると複数ヶ月規模のプロジェクトになる場合があります。一次資料で確認できる標準工数の公表値はないため、外注見積もり時には対象画面数・状態クラス数・既存テスト有無を明示して複数社に提示することを勧めます。
外注パートナー選定の3つのチェックポイント
状態管理リファクタリングの外注は、新機能開発とは異なる専門性を要します。パートナー選定時に確認したい3つのポイントを整理します。
チェック1:採用設計パターンの実績と提案力
「Flutter案件を経験している」だけでなく、「Riverpodへの移行経験がある」「BLoC設計でのコードレビューを担当したことがある」というレベルで実績を確認します。ポートフォリオやGitHubリポジトリへのアクセスを依頼するか、技術的な質問を提案段階で行うことが有効です。
また、提案内容に「なぜそのパターンを選ぶか」の根拠があるかを確認します。「当社はRiverpodを推奨します」という一言だけでなく、「既存コードのProvider依存度・チームの習熟度・テスト環境を踏まえてRiverpodへの段階移行を提案する」という形で根拠が示されるパートナーが信頼性の高い選択肢です。
チェック2:段階移行・テスト実装の進め方
全面書き直しを前提とした提案は、稼働中アプリのリファクタリングとしてリスクが高いです。「機能単位での段階移行」「各フェーズでのテスト実装」「本番適用前のレビューフロー」を提案書に含めているかを確認します。
テストカバレッジの基準も提案段階で提示を求めます。「テストを書きます」という回答だけでなく、カバレッジの目標値(例:新規コード70%以上)と計測方法が示されているかどうかが、成果物の品質を確認する目安になります。
チェック3:元請(プライムベンダー)体制か再委託かの確認
発注先が実装を別の開発会社に再委託している場合、コミュニケーションロスが生じやすく、設計の意図が正確に伝わらないリスクがあります。特に状態管理のリファクタリングのように設計意図の理解が品質に直結する案件では、元請(プライムベンダー)として自社エンジニアが実装を担う体制かを確認することが大切です。
契約前に開発担当エンジニアとの技術的な対話の機会(技術面談)を設けることで、実務レベルの知識と対応力を確認できます。
まとめ:外注で状態管理を整理するための判断軸
本稿では、モバイルアプリの状態管理リファクタリングを外注する進め方について整理しました。要点を3つに集約します。
第一に、リファクタリングは全面刷新ではなく機能単位の段階移行が原則です。稼働中のアプリを止めずに品質を高めるには、設計マッピング→シンプルな機能からの移行→機能ごとのレビューと本番適用→標準化という4ステップが有効です。
第二に、採用するプラットフォームに適した設計パターンを選ぶことが外注成功の前提です。Flutter向けRiverpodのコンパイル時型エラー検出・BuildContext非依存、BLoCのStreamベースイベント駆動設計、iOS向けObservation/Combine+MVVM、Android向けViewModel+StateFlowのそれぞれの特徴を理解したうえで要件定義に落とし込みます。
第三に、外注パートナーには設計パターンの採用根拠、段階移行の進め方、テストカバレッジ基準の明示を求めることが成果物の品質向上につながります。元請(プライムベンダー)として自社エンジニアが実装を担う体制かどうかも確認します。
よくある質問
状態管理のリファクタリングは、パフォーマンス改善と何が違いますか?
状態管理リファクタリングは「コードの内部品質(保守性・テスト容易性・拡張性)を高める設計変更」です。パフォーマンス向上は「画面描画速度・メモリ使用量・通信速度など、ユーザーが体感できる動作速度の向上」を指します。目的が異なるため、同時に進める場合でも目標と成果指標を分けて設定することを勧めます。状態管理を整理することでレンダリングの無駄な再実行が減り、結果としてパフォーマンスが向上するケースはありますが、それはあくまで副次的な効果です。
Flutter以外(iOS・Android)でも外注は対応可能ですか?
対応可能です。iOSではSwift+Observationフレームワーク(iOS 17以降)またはCombine+MVVM、AndroidではKotlin+ViewModel+StateFlowの各スタックに対応できる外注パートナーが存在します。ただし、FlutterはRiverpodやBLoCのように状態管理パターンが多数あり、実績の確認が特に重要です。選定時は対象OS・バージョン・使用ライブラリを明記した要件書を用意することで、見積もり精度が高まります。
リファクタリング中に既存機能が壊れるリスクはどう防ぎますか?
機能単位の段階移行と、各フェーズでのテスト実装が主な防衛手段です。移行前に既存機能の動作を担保するテスト(ゴールデンテスト・ウィジェットテストなど)を用意しておき、移行後に差分がないことを確認する手順を外注パートナーと合意します。また、本番への適用は機能ごとに行い、問題が発生した場合の切り戻し手順を事前に定めておくことで、影響範囲を局所化できます。
外注費用の目安はどのくらいですか?
状態管理リファクタリングの費用は、対象アプリの画面数・状態クラス数・既存テストの有無・採用パターンの複雑度によって大きく変わります。公表された標準費用の一次資料がないため断定的な数値の提示は控えますが、実務上は要件定義・設計マッピングから標準化完了まで含めた総費用として複数社に見積もりを依頼し、提案内容の具体性と費用を比較することを勧めます。費用だけでなく、段階移行の進め方・テストカバレッジ基準・レビュー体制も評価軸に加えてください。
リファクタリング完了後の保守は内製に戻せますか?
設計を標準化し、フォルダ構造・命名規則・アーキテクチャの方針を文書化しておけば、完了後に内製チームへ移管することは現実的な選択肢です。外注パートナーに対して「完了後の引き継ぎドキュメント作成」「社内エンジニアへの技術トランスファー」を契約に明記しておくことで、知識が社内に残る形を設計できます。外注をゴールにするのではなく、内製保守に戻れる状態を最終目標として設定することを勧めます。
著者:テレリモ総研編集部 鈴木 亮佑
ご不明な点はお問い合わせフォームからもご連絡いただけます。
- *1 出典:Riverpod「Riverpod公式サイト」(2024年)
- *2 出典:pub.dev「bloc package — Dart packages」(2024年)
- *3 出典:Android Developers「StateFlow and SharedFlow | Kotlin | Android Developers」(2024年)