
1.「UI は State の関数である」
"The view should be a function of state."
(View / UI というものは、状態の関数であるべきだ。)
UI に対して「表示せよ」「消せ」と個別命令(コマンド)を出すのではなく、ただ「現在の状態(State)」を渡して描画させるべきだという、現代の宣言的UI(Composeなど)の根本思想。
2.「イベントは過去の事実、コマンドは未来への指示」
"Events are things that happened in the past. Commands are instructions for the future. Don’t model events as commands."
(イベントとは『過去に起起きた事実』である。コマンドとは『未来への指示』である。イベントをコマンドとしてモデリングしてはならない。)
画面回転時の二重発火バグなどを防ぐため、UI から上がる「起きた事実(Event)」とロジックからの「操作指示(Command)」を明確に区別せよという警告。
3.「イベントは上がり、ステートは降りてくる」
"Events go up, State goes down."
(イベントは上へ上がり、ステートは下へ降りてくる。)
ユーザー操作(Event)は上層のロジックへ送信され、処理結果として更新された「State」だけが下層の UI へ降りてくる単方向データフロー(UDF)の本質を直感的に表したフレーズ。
4.「View はバカ(受動的)でいい」
"Views are dumb. They should just render whatever state is given to them, and report user events back up."
(View はバカ(受動的)でいい。渡された State をそのまま描画し、発生したユーザーイベントを上層に報告するだけの存在であるべきだ。)
UI 自身に複雑な if/else や描画切り替えロジックを持たせず、渡された State を忠実に描画するだけの存在に削ぎ落とすべしという指針。
5.「State は操作の差分ではなく、純粋なデータから作れ」
"State should be represented as data, not as a delta of commands."
(状態はコマンドの差分(delta)として表現するのではなく、純粋なデータとして表現すべきだ。)
「UI要素を少しずつ操作する差分積み上げ」ではなく、「画面全体のデータ構造(Data Class)」を一括で差し替えるべきだという命令型UIへの批判。
6.「Compose は UI ツールキットではなく、State の管理エンジンだ」
"Jetpack Compose is not a UI toolkit. It's an unbundled compiler plugin and runtime for managing a tree of nodes from state."
(Jetpack Compose は UI トゥールキットではない。State(状態)からノードのツリーを管理するための、独立したコンパイラプラグイン兼ランタイムだ。)
Compose の本質は「State の変化に連動してツリー構造を自動更新する汎用エンジン」であり、UI 以外のロジック層(MoleculeやCircuit)にも応用できるという洞察。
7.「Molecule: Compose ランタイムで StateFlow を生み出す」
"Molecule uses Jetpack Compose under the hood to convert any State / Action loop into a single StateFlow."
(Molecule は裏側で Jetpack Compose を使い、あらゆる State / Action のループを単一の StateFlow へと変換する。)
ViewModel 内で複雑な Flow をこねくり回す代わりに、Compose の宣言的構文(remember や LaunchedEffect)を使ってロジック層の State を生成するという新たなアプローチ。
8.「State が Mutable なら、そこにあるのはカオスだ」
"If your state is mutable, you don't have a single source of truth. You have chaos."
(もし君の状態(State)が可変(Mutable)なら、そこにあるのは『唯一の正しい情報源(Single Source of Truth)』ではなく、ただの『カオス』だ。)
UI 側が State を直接書き換える危険性を排除し、State は必ず Immutable(不変)に保ち、更新はインスタンスの再生成で行うべきだという強調。
9.「View を描画せずに UI ロジックをテストすることこそが聖杯だ」
"Testing UI logic without rendering views is the holy grail of modern UI architecture."
(View を描画することなく UI ロジックをテストすることこそが、現代の UI アーキテクチャの聖杯(悲願)だ。)
Circuit などの設計理念。重い Android 端末やエミュレータを起動せず、純粋な JVM の高速な単体テストで UI ロジックを完結させる重要性。
10.「状態の再現(Recreation)と永続化(Persistence)を混同するな」
"State restoration after process death is not the same as data persistence. One is temporary UI continuity, the other is long-term storage."
(プロセス死後の状態復元は、データの永続化とは異なる。前者は一時的な UI の継続性であり、後者は長期的なストレージ保存だ。)
SavedStateHandle(あるいは旧 onSaveInstanceState)に保存すべき「画面の途中経過(スクロール位置や未送信の入力文言)」と、データベースや DataStore に保存すべき「アプリの永続データ」を明確に分けよという設計指針。
11. 「副作用(Side Effect)を状態(State)の中に紛れ込ませるな」
"Side effects should be explicit responses to state changes, not hidden steps inside state mutations."
(副作用は状態の変化に対する明示的な反応であるべきであり、状態の変更処理の中に隠されたステップであってはならない。)
データベースの書き込みやネットワークリクエストといった「副作用」を、State の更新ロジック(Reducer や Setter)の中にコッソリ埋め込むなという注意喚起。Side Effect は State の変化(あるいは操作イベント)を受けて独立して発火させるべきだと述べました。
12.「状態を暗黙的に共有するな。明示的に流せ」
"Implicit shared state is the root of all evil in UI development. Pass state explicitly down the tree."
(暗黙的に共有された状態は、UI 開発におけるあらゆる悪の根源だ。状態はツリーを伝って明示的に下に渡せ。)
グローバルなシングルトンや静的変数に状態を持たせてあちこちから参照・書き換えをする設計を強力に否定。現在で言えば、Compose の LocalContext や CompositionLocal にビジネス状態を安易に突っ込まず、パラメータ(Props)として明示的に State を渡す「State Hoisting」の重要性に通じます。
13.「状態の変更経路(Mutation Path)は常に1つに絞れ」
"There should only ever be one pathway through which a piece of state can change."
(ある1つの状態が変化する経路は、常に1つだけでなければならない。)
いわゆる Single Source of Truth(唯一の正しい情報源) と Unidirectional Data Flow(単方向データフロー) の根幹。複数の場所から状態を書き換えられるようにすると、どのイベントが原因で UI が壊れたのか追跡不能(カオス)になるという指摘です。
14.「UI テストが壊れやすいのは、View に状態を持たせているからだ」
"Fragile UI tests are a symptom of UI components holding and managing their own implicit state."
(壊れやすい UI テストは、UI コンポーネント自体が暗黙の状態を保持・管理してしまっていることの徴候だ。)
Espresso や UI テストが少しの変更で落ちやすくなる原因は、UI(View/Composable)自体に「状態の変化ロジック」が残っているからであり、UI を純粋な「State を受け取って表示するだけの関数」にできれば、ロジックのテストは超高速な Unit Test だけで完結すると説きました。
15.「非同期処理のキャンセル(Cancellation)も State の一部だ」
"Cancellation is not an error; it's a first-class state transition."
(キャンセル処理はエラーではない。ファーストクラスの『状態遷移』である。)
画面遷移やスクロールによって不要になった通信や処理をキャンセルすることは、例外処理(Exception)として捉えるのではなく、「処理中」から「破棄/無効」という正常な状態の遷移として設計・ハンドリングすべきだという主張。
16.「状態の集約(Aggregation)は、UI に届く前に終わらせろ」
"Do not make the UI assemble state from multiple streams. Combine them in the logic layer and expose one unified State."
(UI に複数のストリームから状態を組み立てさせるな。ロジック層でそれらを結合(Combine)し、単一の統一された State として公開せよ。)
Composable や View の中で streamA と streamB をそれぞれ Observe して if (a && b) のようなロジックを書くのを禁止する教義。combine(flowA, flowB) { a, b -> UiState(...) } のように、ViewModel/Presenter 側で1つの UiState にまとめ上げてから UI に渡すべしという指針。
17.「StateFlow は最新の値(State)を持ち、SharedFlow は出来事(Event)を流す」
"StateFlow represents a value over time that can be read at any moment. SharedFlow is for broadcasting events where history may not matter."
(StateFlow は常に読み取れる『時間経過に伴う値』を表す。SharedFlow は履歴が重要ではない『出来事のブロードキャスト』のためにある。)
Coroutines Flow 導入期に、開発者が StateFlow と SharedFlow の使い分けに混乱していた時期の発言。状態(State)は常に「現在の最新値」を保持・復元できる必要があり、イベント(Event)は「通過するストリーム」であるという本質的な違いを明確化しました。
18.「可変性(Mutability)は関数の中に閉じ込めろ」
"Local mutability is completely fine. Shared mutability is poison."
(関数ローカルな可変性は何の問題もない。共有された可変性こそが毒なのだ。)
「一切の var や可変オブジェクトを排除せよ」という極端な関数型プログラミング信者に対する現実的なアドバイス。関数内部でループや計算のために var や MutableList を使うのは何の問題もないが、それが関数の外にリーク(共有)した瞬間にバグの温床(カオス)になるという指摘です。
19.「状態の表現(Types)にドメインの制約(Invariants)を埋め込め」
"Make invalid states unrepresentable in your type system."
(型システムによって『不正な状態』を表現不可能にせよ。)
例えば data class UiState(val isLoading: Boolean, val data: Data?, val error: Throwable?) だと「ロード中なのにデータもエラーもある」という不正な状態(Invalid State)が作れてしまう。これを sealed class や sealed interface を使って Loading, Success(data), Error(err) に分離することで、物理的に「不正な状態になり得ない型定義」にせよという教義。
20.「アーキテクチャとは『どこに状態(State)を置くか』の決定にすぎない」
"Architecture is fundamentally just deciding where state lives and how it moves."
(アーキテクチャとは、根本的には『状態がどこに生存し、それがどう移動するか』を決めることにすぎない。)
設計パターン(MVVM, MVI, Clean Architecture など)の複雑な層構造や命名に惑わされている開発者に向けた本質的な一言。結局のところ、状態がどこで作られ、どこに保存され、どう流れるか(UDF)を整理することこそがアーキテクチャの真髄であるとまとめました。
21.「暗黙の副作用(Implicit Side-effects)はデバッグの敵だ」
"If an operation changes state, it must be explicit. Hidden state changes are the hardest bugs to trace."
(操作が状態を変更するなら、それは明示的でなければならない。隠された状態変更こそが、最も追跡困難なバグだ。)
プロパティの getter や setter の内部、あるいは「単なる読み取り用の関数」に見える場所で、裏でこっそり内部フラグや状態を書き換えるコードを厳しく戒めた発言。状態の変化(State Mutation)は必ず明示的な関数名やイベントハンドラを通じて行われるべきだと主張しました。
22.「UI レイヤーに 『時間の概念』を持ち込むな」
"The UI should not care about time. Debouncing, throttling, and delay belong to the state pipeline, not the View."
(UI は 『時間』の概念を気にすべきではない。デバウンス(Debounce)やスロットリング(Throttle)、ディレイ処理は View ではなく State のパイプラインに属するものだ。)
連打防止のための Handler.postDelayed や Compose 内での delay() を使ったボタン制御などを否定。検索窓のインクリメンタルサーチやボタンの連打制御など、「時間」が絡むロジックはすべて ViewModel / Presenter 側の Flow パイプライン(debounce(), sample() など)で処理し、UI には「今表示すべき State」だけを渡すべきだという教義です。
23.「状態の生存期間(Scope)を UI の生存期間と一致させるな」
"Do not tie the scope of your state to the lifespan of a single UI component, unless that state dies with it."
(その状態が UI と一緒に死ぬものでない限り、状態のスコープを単一 UI コンポーネントの生存期間に縛り付けるな。)
Fragment や Composable の「画面破棄」と「画面背後で進行している処理(ダウンロードや送信処理)」のスコープを切り離す設計の重要性。UI が回転やスタック遷移で消えても、状態を管理するスコープ(ViewModelScope や CoroutineScope)は独立して生存し、再び UI が復元された際に最新の State を再注入(Re-attach)できる構造にせよと説きました。
24.「宣言的 UI の真価は Recomposition(再構築)の予測可能性にある」
"Declarative UI is not just less code; it’s making UI updates predictable by reducing state space."
(宣言的 UI の本質は単にコード量が減ることではない。状態空間(State Space)を削減することで、UI の更新を予測可能にすることだ。)
Compose などの宣言的 UI が優れている最大の理由は「コードの短さ」ではなく、if/else や手動の UI 更新命令で発生しがちだった「あり得ない画面状態
(不整合な組み合わせ)」を排除し、State から UI へのマッピングを 1:1 の決定論的(Deterministic)な関数にすることだと述べました。
25.「単方向(Unidirectional)とは、逆流を許さないということだ」
"Unidirectional data flow means data flows one way. If your view is updating the model directly, your flow is bidirectional and broken."
(単方向データフローとは、データが一方向にしか流れないことを意味する。もし View が Model を直接更新しているなら、それは双方向(Bidirectional)であり、崩壊している。)
2-way Data Binding(双方向データバインディング)のように、UI 側のテキスト入力が自動的に Model のプロパティを直接書き換える仕組みへの強烈な牽制。UI はあくまで「テキストが変更された」という Event を発行し、それを受けたロジック層が新しい State を生成して下に降ろすという「一方向の円環」を崩してはならないと emphasized しました。
26.「表示の遅延(Latency)も State としてデザインせよ」
"Loading is not a lack of data; it is a discrete, valid state of your UI."
(ローディング(読み込み中)は『データが無い状態』ではない。それ自体が独立した、正しい UI の『状態』である。)
データを取得するまでの待ち時間や遅延を「データの不在(null)」として扱うのではなく、明確に設計された State.Loading という状態としてモデリングすべきだという主張。data == null でローディングを判定するようなズボラなコードが、Skeleton View やプログレス表示のチラつき(Flicker)バグを生む原因になると戒めました。
27.「状態の同期(Synchronization)に頼るな。計算(Computation)で導出せよ」
"Don't synchronize two states if you can derive one from the other."
(一方から導出できるのであれば、2つの状態を『手動で同期』させようとするな。)
例えば「リストの全要素(items)」と「検索結果の要素数(count)」という2つの変数を持たせて手動で count を更新するようなコードへの批判。count は items.size から派生して計算(Derive)されるべきものであり、複数の状態を手動で同期させようとした瞬間に必ず同期漏れのバグが発生すると指摘しました。
28.「Redwood: 状態のツリーをマルチプラットフォームで共有する」
"Redwood applies the same Compose state-to-tree paradigm to arbitrary UI toolkits across Android, iOS, and Desktop."
(Redwood は、Compose の『State からツリーを生成するパラダイム』を、Android, iOS, Desktop を跨ぐ任意の UI トゥールキットに適用するものだ。)
Jake が開発をリードした Cash App Redwood(のちに Circuit の下地にもなったライブラリ)の思想。State から作られる UI ツリーの構築ロジックを KMP(Kotlin Multiplatform)としてロジック層に完全に切り離せば、描画エンジンが Android Native(View/Compose)であれ iOS(SwiftUI)であれ、完全に同じ State / Logic を共有できるという宣言的UIの究極系を示しました。
29.「エラー状態は特殊な分岐ではなく、State の変奏曲(Variant)にすぎない」
"Errors in UI are not control flow exceptions; they are just data waiting to be rendered."
(UI におけるエラーは制御フローの例外(Exception)ではない。ただ描画されるのを待っている『データ』にすぎない。)
API 通信などの例外が発生した際、try-catch で UI の表示を強引に切り替える(制御フローとして扱う)のを禁止する考え方。例外が発生した場合も、それを捕捉して State.Error(message) という純粋な「データ(State)」に変換して UI に渡すべきであり、UI 側はエラーすらも「描画すべきデータの種類のひとつ」としてフラットに処理すべきだと説きました。
30.「アーキテクチャの良し悪しは、状態(State)を巻き戻せるか(Time Travel)で測れる」
"A clean architecture allows you to replay a stream of events and recreate the exact UI state at any point in time."
(優れたアーキテクチャとは、イベントのストリームを再生(Replay)することで、過去の任意の時点における UI 状態を正確に再構築できるものである。)
MVI / UDF の最大の強みであるデバッグ可能性(Time Travel Debugging)についての発言。すべての状態が「初期 State + イベントの累積」で計算可能になっていれば、起きたイベントのログを順番に叩き込むだけで、ユーザーの手元で起きたバグの画面状態を開発環境で 100% 完全再現できると説明しました。
31.「State の比較(Equals)は安く、UI の再描画(Recomposition)は重い」
"State comparison is cheap; rendering UI is expensive. Always design your states so that distinct structural equality skips unnecessary redrawing."
(State 同士の比較(equals)は軽量だが、UI の描画はコストが高い。構造的等価性(data class の == 比較)によって不要な再描画をスキップできるよう、常に State を設計せよ。)
Compose などの宣言的 UI において、State が data class や @Stable / @Immutable に適切に定義されていれば、フレームワークは「State が変わっていない」と判断して描画スキップ(Skipping)を完璧に行えるというパフォーマンス原則。State の定義をテボラに作ると、画面全体の無駄な Recomposition を引き起こす原因になると警告しました。
32.「データベースは、State の『永続化されたプロキシ』に過ぎない」
"A database table is not a entity separate from your UI; it's just a persisted, observable stream of state."
(データベースのテーブルは UI と分離された別物ではない。ただの『永続化され、観察可能な State のストリーム』に過ぎない。)
彼が手がけた SQLDelight の設計思想。DB は「クエリを発行してデータを抜き取る場所」ではなく、「DB の変更を Flow / Observable として監視(Observe)し、そのまま State パイプラインに流し込む元泉(Single Source of Truth)」として扱うべきだと主張しました。
33.「状態(State)をモジュール境界(Module Boundaries)にするな」
"Expose behavior and events across feature modules, not raw mutable state structures."
(機能モジュール間(Feature Modules)で公開すべきなのは『振る舞い(Behavior)とイベント(Event)』であり、生の可変状態構造(Mutable State)ではない。)
マルチモジュール構成における警告。ある Feature モジュールが別の Feature モジュールの内部 State や ViewModel に直接依存してしまうと、モジュール間の結合度が跳ね上がり、ビルド時間の悪化や状態の衝突を引き起こす。モジュール境界を跨ぐときは純粋な Interface や Event / Intent のやり取りに留めるべきだと説きました。
34.「画面(Screen)単位で ViewModel を刻むな。機能(Feature)の境界で刻め」
"ViewModels shouldn't blindly map 1:1 to every single XML or Composable file. They map to boundaries of state ownership."
(ViewModel をすべての XML や Composable ファイルに機械的に 1:1 で対応させるな。ViewModel は『状態の所有権(State Ownership)の境界』に対応させるものだ。)
「1画面=1 ViewModel」という固定観念に対する指摘。小さなコンポーネントや単なるリレイアウト部分にまで ViewModel を作成すると管理が煩雑になる。ViewModel / State Holder は、明確に自律したビジネスロジックや状態の持ち主(State Owner)ごとに割り当てるべきだと説明しました。
35.「状態の「型(Type)」をケチるな。明確な名前(Explicit Naming)を与えよ」
"Don't reuse generic tuples or Pair/Triple to represent UI state. Give every state a clear, domain-specific name."
(UI State を表現するために、汎用的な Pair や Triple、Tuple を使い回すな。すべての State に明確でドメイン固有の名前を与えよ。)
Pair<Boolean, List<Item>> のような手を抜いた型定義を厳禁とする教義。型自体がドメインの文脈 SearchUiState(val isSearching: Boolean, val results: List<Item>) を自己説明していなければ、コードの可読性が失われ、State パイプラインの追跡が困難になると強調しました。
36.「状態(State)はシリアライズ(直列化)できなければ本物ではない」
"If your state cannot be serialized, it’s not truly state; it’s just transient in-memory behavior."
(もし君の状態(State)がシリアライズできないのなら、それは真の状態ではない。ただの過渡的なメモリ上の振る舞いにすぎない。)
プロセス死(Process Death)や画面遷移のスタック管理において、UI State や Navigation State は kotlinx.serialization や Parcelable などで復元可能なデータ構造(Value Class / Data Class)として定義されるべきだという主張。ラムダ式やコンテキスト(Context)といった「復元不可能なオブジェクト」を State に混ぜ込むなという警告です。
37.「非同期ストリームの『完了(Completion)』と『状態(State)』を混同するな」
"A StateFlow never completes. If your flow completes, it’s representing a task, not a state."
(StateFlow は決して完了しない。もし君の Flow が完了するなら、それは『状態』ではなく『タスク(処理)』を表している。)
Coroutines における根本的な概念の分離。データ通信や計算タスク(Task)は終了(Complete/Success/Error)を迎えますが、画面やコンポーネントが生きている限り、それを監視する StateFlow は常に「現在の値」を提示し続ける無限のストリームであるべきだと指摘しました。
38.「State をグローバルに叫ぶな。局所化(Locality)せよ」
"Scope your state to the exact boundary of who needs it. Globalizing state is just lazy engineering."
(状態はそれを必要とする最小限の境界の中にスコープせよ。状態をグローバル化するのは単なる怠惰なエンジニアリングだ。)
アプリ全体の共通領域(Singleton や GlobalScope)に安易に State を置く設計への批判。「画面全体の State」「コンポーネントごとの State」「ダイアログの State」など、各ツリーのノードが最小限のスコープで責任を持って状態を保持すべきだと述べました。
39.「テストコードのセットアップが長すぎるのは、State が肥大化(Fat)している悲鳴だ」
"If your test setup requires mocking ten dependencies to verify a state change, your state holder knows too much."
(状態の変化を検証するだけで10個もの依存をモックしなければならないなら、君の State Holder(ViewModel/Presenter)は知りすぎている。)
テストの書きやすさを設計の良悪のバロメーターとする教義。Presenter や ViewModel が大きくなりすぎている(Fat)場合、小さな State / Event のサブユニットに分割するか、Use Case や Reducer として切り出すべきだというアドバイスです。
40.「状態(State)は『過去の履歴』を持つな。『現在の事実』だけを持て」
"State represents the present. Do not pollute state with event history unless the history itself is part of the domain requirement."
(State は『現在』を表すものだ。ドキュメントの履歴表示などドメイン要件自体が歴史を求めている場合を除き、過去のイベント履歴で State を汚染するな。)
「過去にどのボタンが押されたか」「どのエラーが通過したか」といった履歴情報を State のプロパティとして引きずり回すアンチパターンへの指摘。State は常に「たった今、UI がどう描画されるべきか」という現在値のみを保持すべきであると説きました。
41.「State の型に非ドメインな構造(UI フレームワーク要素)を混ぜるな」
"Never leak framework-specific types into your State. Your State should be pure Kotlin, completely detached from Android or iOS."
(State の中にフレームワーク固有の型をリークさせるな。State は完全に Android や iOS から独立した、純粋な Kotlin であるべきだ。)
UiState のプロパティに Bitmap, Drawable, Color, Context といった Android API 固有のオブジェクトを持たせるなという徹底した教義。これらを混ぜると、JVM 上での超高速な単体テストが不可能になり、Kotlin Multiplatform (KMP) への移植性も失われます。
42.「初期状態(Initial State)の定義を曖昧にするな」
"Every State pipeline must have a deterministic, synchronous initial value before any async work begins."
(あらゆる State パイプラインは、非同期処理が始まる前に、決定論的かつ同期的に読み取れる『初期値』を持たなければならない。)
StateFlow が initialValue を強制する理由であり、remember や collectAsState が初期値を求める理由。非同期通信が完了するまでの「コンパイル可能で破綻しない初期 State(例: Loading や空の状態)」を定義できない設計は、画面初期化時のヌルポやチラつき(Flicker)の原罪になると指摘しました。
43.「状態の階層構造(State Nesting)は浅く保て」
"Prefer flat State classes over deeply nested structures. Flat state makes copying and recomposition tracking dramatically simpler."
(深くネストされた構造よりも、フラットな State クラスを好め。フラットな State は copy() や Recomposition の追跡を圧倒的にシンプルにする。)
state.user.profile.address.city のように State の中にオブジェクトを深層ネストさせると、一部分を更新する際に copy(user = user.copy(profile = ...)) とコードが惨状と化し、Recomposition の不要な発火を引き起こしやすい。状態の持ち方は可能な限りフラットなデータ構造にまとめよと教え示しました。
44.「状態の変化(State Change)と、表示のアニメーション(Animation)を混ぜるな」
"Animation is a rendering concern. State dictates 'what is shown', while the UI layer decides 'how it transitions'."
(アニメーションは描画の関心事だ。State は『何が表示されているか』を命じ、UI レイヤーが『どう遷移(アニメーション)するか』を決定する。)
State の中に animatingIn: Boolean や fadeInProgress: Float といったアニメーションの途中経過プロパティを持たせる設計への否定。State はあくまで「現在の論理的な表示状態(例: Visible / Hidden)」を保持し、それをどう画面上で動かすか(AnimatedVisibility など)は UI 側のレンダリングロジックに委ねるべきだと主張しました。
45.「遷移(Navigation)とは、本質的に『Navigation State』の変更にすぎない」
"Navigation is not about switching screens; it's about mutating a backstack state value in a predictable stack."
(ナビゲーションとは『画面を切り替えること』ではない。予測可能なスタックにおける『バックスタック状態(State)の値の書き換え』である。)
画面遷移を「Activity や Fragment のインテント発火(Command)」と捉えるのではなく、「画面の履歴スタック(List
46.「副作用(Effect)は消費されるべきものであり、観察(Observe)されるべきものではない」
"Events and side-effects are meant to be consumed once, while state is meant to be observed continuously."
(イベントや副作用は『1回消費されるべきもの』であり、ステートのように『継続的に観察され続けるもの』ではない。)
StateFlow(継続観察)と Channel / SharedFlow(1回消費)の決定的な役割の違いを突いた名言。一回限りのアクション(Toast や Analytics 送信など)を継続観察可能な State に混ぜ込むと、再描画(Recomposition)や画面回転のたびに副作用が意図せず再発火する根本原因になると説きました。
47.「ドメインモデルを直接 UI State に渡すな。表示用の防波堤を作れ」
"Do not pass your raw domain or network models straight into the UI. Map them to a tailored UI State."
(生(Raw)のドメインモデルやネットワークモデルをそのまま UI に渡すな。UI 専用に調整された UI State にマッピングせよ。)
API のレスポンス型や DB の Entity 型をそのまま Composable や View に引数として流し込むズボラな設計に対する警告。ドメインモデルの仕様変更(フィールド名の変更や型の追加)が直接 UI 描画の崩壊に直結するのを防ぐため、必ず UI 層の手前で UiState へマッピングする「層の分離(Decoupling)」を徹底すべしと求めました。
48.「UI の『状態の崩壊』は、暗黙的なデフォルト値(Implicit Default)から始まる」
"Avoid implicit defaults in your state models. Explicit initial conditions prevent sneaky UI race conditions."
(State モデル内で暗黙的なデフォルト値("" や 0 や false)を多用するな。明示的な初期状態の定義こそが、忍び寄る UI のレースコンディション(競合)を防ぐ。)
data class UiState(val isError: Boolean = false, val data: String = "") のように安易なデフォルト値を持たせると、「まだ初期化されていない状態」と「正常に空データが取得できた状態」の区別がつかなくなり、初期化時のチラつきやレースコンディションを招く。初期状態は Loading などの型で明示的に区別すべきだと強調しました。
49.「State Holder に UI フレームワークのコンテキスト(Context)を渡すな」
"If your ViewModel or Presenter needs an Android Context, your state design has failed."
(もし君の ViewModel や Presenter が Android の Context を必要としているなら、状態の設計は失敗している。)
AndroidViewModel のように Context(や Resource)を直接ロジック層に持ち込むアプローチへの痛烈な批判。文字列のリソース ID(R.string.x)や文字列フォーマットの解決は、ViewModel 側で String 型のテキストデータ(またはリソースをカプセル化した Wrapper)に変換してから UI に渡すか、UI 描画時(Compose 側)に解決させるべきであり、ロジック層をフレームワーク依存から守るべきだと説きました。
50.「アーキテクチャの真価は、新しい機能を足す時ではなく、古い状態(State)を破棄する時にわかる」
"The test of a good architecture is not how easily you can add state, but how safely you can discard it."
(優れたアーキテクチャの真価は、どれだけ簡単に状態を追加できるかではなく、どれだけ安全に不要な状態を破棄(Discard / Clean up)できるかで試される。)
画面を離脱した際や機能を使わなくなった際、保持していたメモリ、非同期タスク、キャッシュなどの State が漏れ(Memory Leak)なく確実にクリアされる構造になっているか。状態の追加だけでなく「安全なクリーンアップ(onCleared / Coroutines のキャンセル)」まで見越した単方向設計こそが、プロダクトを息長く存続させると締めくくりました。
🤔 まとめ
1. UI = f(State) の徹底: UI は状態を受け取って描画するだけの単なる「関数」に過ぎない。
2. 命令(Command)から状態(State)への転換: UI のメソッドを叩いて操作指示を出す設計を全廃する。
3. データフローの単方向化(UDF): イベント(上り)とステート(下り)を明確に分け、逆流を許さない。
4. 型システムによる安全性: 不正な状態を型レベルで表現不能にし、シリアライズ可能・Immutable に保つ。
5. フレームワークからの解放: State や Logic を純粋な Kotlin コードに保つことで、超高速な Unit Test と KMP(Multiplatform)への展開を実現する。
この50個の教義を意識して Compose や ViewModel / Circuit を設計することで、バグがなくメンテナンス性に富んだ現代的な Android / Multiplatform アプリケーションを構築することができます。


