TECH PLAY

Firebase

イベント

マガジン

該当するコンテンツが見つかりませんでした

技術ブログ

.table-of-contents > li > ul { display: none; } こんにちは、MA部配信基盤ブロックの田島です。ZOZOTOWNではユーザへのコミュニケーション手段の1つとしてアプリへのPush通知を活用しており、配信にはFirebase Cloud Messaging(以降、FCM)を利用しています。 FCMではPush通知の送信先となるデバイスごとに「FCMトークン」と呼ばれる一意の識別子が発行され、このトークンを宛先としてFCMにリクエストを行うことで、特定のデバイスにPush通知が届きます。 FCMでは無効なトークンに対して UNREGISTERED エラーを返します。Firebaseの公式ドキュメントでは、このエラーが返されたトークンを無効として扱うことが推奨されています。しかし、我々の調査により、 一度 UNREGISTERED エラーを受けたトークンがその後復活し、再び有効になるケース の存在を確認しました。復活したトークンで配信すると正常にPush通知が届きクリックイベントも取得できることから、確実に有効なトークンであることを確認しています。 本記事では、このトークン復活の実態調査と、FCMの validate_only APIを活用したエラートークン管理の精緻化について紹介します。 目次 目次 背景と課題 FCMトークンとは 既存のエラートークン管理の問題 エラートークン復活の調査 調査内容 調査方法 調査結果 トークン復活に関する補足 方針の検討 方針1: 一定期間UNREGISTEREDが続いたトークンをエラー扱い 方針2: 即時エラー登録 + validate_onlyで定期解除 決定した方針 FCM validate_only フラグを利用したトークンの検証 validate_only フラグ 動作検証 エラートークンの収集と検証バッチの実装 テーブル設計 エラートークンテーブル(error_fcm_tokens) 再有効化テーブル(reactivated_fcm_tokens) エラートークンの収集・再検証ワークフロー 1. エラートークンの収集 2. 検証用一時テーブルの作成 3. エラートークンの再検証(並列処理) 検証対象トークンの取得 シャード単位の検証処理 FCM APIによるトークン検証 4. エラートークンテーブルの更新 パフォーマンス 既存の全トークンの再検証と本番リリース 初回実行:全期間のエラートークンを検証 通常運用の開始 まとめ 最後に 背景と課題 FCMトークンとは 最初にも紹介しましたが、FCMトークンとは、FCMがPush通知の送信先を識別するために、アプリがインストールされた各デバイスに対して発行する一意の識別子です。アプリの初回起動時にFCM SDKがこのトークンを生成し、このトークンを指定してFCMにリクエストを行うことで、特定のデバイスにPush通知が届きます。 配信フローとしては、サーバからFCMにメッセージリクエストが送られます。FCMはプラットフォーム固有の転送層(Androidの場合はATL、iOSの場合はAPNs)を経由して対象デバイスにメッセージを届けます。 FCMトークンは永続的でなく、以下のような理由で無効化や更新が発生します。 トークンがリフレッシュされた場合 トークンの保持期間を超過した場合 アプリがアンインストールされた場合 無効になったトークンを使ってFCMにリクエストを行うと、 UNREGISTERED エラーが返されます。 firebase.google.com 既存のエラートークン管理の問題 Firebaseの公式ドキュメントでは、 UNREGISTERED エラーが返されたトークンを無効として扱うことがベストプラクティスとして紹介されています。 firebase.google.com こちらに則り、 UNREGISTERED エラーが返されたトークンを無効として記録し、以降の配信対象から除外していました。しかし、 UNREGISTERED エラーを受けたトークンがその後再び有効になるケースの存在を確認しました。この場合、本来配信すべきユーザにPush通知が届かなくなってしまいます。 まずはユーザへの配信が確実にできることを優先し、エラートークンの登録処理を一時的に停止した上で、復活の頻度や傾向を正確に把握するための調査を実施しました。 エラートークン復活の調査 調査内容 エラートークンの管理方針を決めるにあたり、以下の点を調査しました。 SUCCESS → UNREGISTERED → SUCCESS が発生する頻度 UNREGISTERED が何回連続した後 SUCCESS へ復帰するケースがあるか UNREGISTERED がどれくらいの期間続いた後 SUCCESS へ復帰するケースがあるか SUCCESS に復帰後、どれくらいの回数成功が続くか 調査方法 約2.5か月分(2025年8月以降)の配信ログを対象に、同一トークンにおけるステータス遷移を分析しました。 分析に使用した push_logs テーブルは、Push通知の配信結果を1リクエストごとに記録したログテーブルです。主なカラムは以下の通りです。 カラム名 型 説明 token STRING 配信先のFCMトークン delivered_at TIMESTAMP 配信日時 status STRING 配信結果( SUCCESS , FAILED ) status_detail STRING 失敗時の詳細( UNREGISTERED など) fcm_message_id STRING FCMが発行したメッセージID 以下のクエリで、 SUCCESS と SUCCESS の間に UNREGISTERED が挟まるケースを抽出しています。 WITH base AS ( SELECT token, delivered_at, status, status_detail, fcm_message_id FROM `project.dataset.push_logs` WHERE TIMESTAMP_TRUNC(delivered_at, DAY) >= TIMESTAMP ( " 2025-08-01 " ) AND TIMESTAMP_TRUNC(delivered_at, DAY) <= TIMESTAMP ( " 2025-10-15 " ) AND token IS NOT NULL AND status IN ( ' SUCCESS ' , ' FAILED ' ) ), -- トークンごとに時系列でインデックスを付与 ordered AS ( SELECT token, delivered_at, status, status_detail, fcm_message_id, ROW_NUMBER() OVER (PARTITION BY token ORDER BY delivered_at, fcm_message_id) AS rn FROM base ), -- 累積のUNREGISTERED失敗数などを付与 ord AS ( SELECT o.*, SUM ( CASE WHEN o.status = ' FAILED ' AND o.status_detail = ' UNREGISTERED ' THEN 1 ELSE 0 END ) OVER (PARTITION BY o.token ORDER BY o.rn ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS cum_unreg_failed, MIN ( IF (o.status != ' SUCCESS ' , o.rn, NULL )) OVER (PARTITION BY o.token ORDER BY o.rn ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS next_non_success_rn, COUNT (*) OVER (PARTITION BY o.token) AS total_rows FROM ordered o ), -- SUCCESS行から直前のSUCCESSとの関係を取得 success_pairs AS ( SELECT s.token, s.rn AS success_rn, s.delivered_at AS success_at, LAG(s.rn) OVER (PARTITION BY s.token ORDER BY s.rn) AS prev_success_rn, LAG(s.delivered_at) OVER (PARTITION BY s.token ORDER BY s.rn) AS prev_success_at FROM ord s WHERE s.status = ' SUCCESS ' ), -- 直前SUCCESS〜今回SUCCESSの間にあるUNREGISTERED失敗件数を算出 final AS ( SELECT sp.token, sp.prev_success_at, sp.success_at AS recover_success_at, (oc.cum_unreg_failed - COALESCE (op.cum_unreg_failed, 0 )) AS unreg_failed_between_successes, ( COALESCE (oc.next_non_success_rn, oc.total_rows + 1 ) - oc.rn) AS consecutive_success_count_after FROM success_pairs sp JOIN ord oc ON oc.token = sp.token AND oc.rn = sp.success_rn LEFT JOIN ord op ON op.token = sp.token AND op.rn = sp.prev_success_rn WHERE sp.prev_success_rn IS NOT NULL ) SELECT * FROM final WHERE unreg_failed_between_successes > 0 ORDER BY recover_success_at; 調査結果 項目 結果 SUCCESS → UNREGISTERED → SUCCESS の発生頻度 2.5か月で約230件 UNREGISTERED の最大の連続回数 約80回 UNREGISTERED が続く最大期間 約14日 SUCCESS 復帰後の成功回数 ケースにより異なる この結果から、 UNREGISTERED の返されたトークンが復活するケースは確かに存在することがわかりました。また、 UNREGISTERED の連続する回数・期間も把握できました。 トークン復活に関する補足 FCMの公式ドキュメントでは、 UNREGISTERED エラーが返されたトークンについて「it will never again be valid(二度と有効にはならない)」と明記されています。そのため、即座に削除することが推奨されています。 firebase.google.com ただし、FCMのエラーコードに関するドキュメントでは「This usually means that the token used is no longer valid and a new one must be used.(通常、使用されたトークンはもはや有効ではなく、新しいトークンを使用する必要があることを意味します)」という表現になっており、「usually」という留保がついています。 firebase.google.com 実際に復活したトークンを使って配信すると正常にPush通知が届き、クリックイベントも取得できることを確認しています。トークンがリフレッシュされて新しいものが発行されたわけでもなく、同一のトークンがそのまま再び有効になっていました。公式ドキュメントの記述と実際の挙動に乖離がある状況です。 なお、この挙動は2026年3月時点でも確認されています。将来的にFCM側で修正される可能性もあるため、最新の挙動については各自で検証されることをお勧めします。 方針の検討 調査により、トークンの復活は2.5か月で約230件と少数ながら確実に発生しており、最長で約14日間 UNREGISTERED が続いた後に復活するケースも確認されました。 この結果を踏まえると、エラートークンの管理には以下の2点を両立させる必要があります。 無効なトークンへの無駄な配信を早期に止めること 復活する可能性のあるトークンを誤って永久に除外しないこと これらを考慮し、以下の2つの方針を検討しました。 方針1: 一定期間UNREGISTEREDが続いたトークンをエラー扱い 1つめの方針は、1か月ずっと UNREGISTERED となっているトークンをエラー扱いにする方式です。調査結果から14日以上 UNREGISTERED が続くケースはなかったため、1か月の閾値で安全にエラートークンを判定できます。これにより、本当に無効となったトークンのみをエラートークンとして保持できます。 方針2: 即時エラー登録 + validate_onlyで定期解除 従来通り UNREGISTERED になったトークンをエラー扱いとしつつ、定期的に validate_only でトークンの有効性を再検証し、復活したトークンをエラーリストから除外する方式です。 validate_only については後ほど説明します。これにより、無効と判定したトークンを即時無効にしつつ、復活したトークンに対しても配信を継続できます。 決定した方針 両方針を比較した結果、以下の理由から 方針2 を採用しました。 方針1だと、一度 UNREGISTERED となったトークンが復活しない場合、1か月の間無効なトークンに配信し続けてしまう 初回の validate_only 検証を既存の全エラートークンに実施することで、これまでに蓄積したエラートークンを有効活用できる 既存のエラートークン登録フローを大きく変更する必要がない FCM validate_only フラグを利用したトークンの検証 validate_only フラグ FCMの messages.send API(FCMにPush通知送信を依頼するAPI)には validate_only フラグがあります。これを true に設定すると、実際にメッセージを配信せずにトークンの有効性のみを検証できます。 動作検証 validate_only (Firebase Admin SDKでは dry_run パラメータに対応)が本当に配信しないことを事前に検証しました。 dry_run=True の場合、レスポンスの message_id が fake_message_id となり、実際のメッセージ配信は行われません。これにより、安全にトークンの有効性を確認できることが実証されました。 dry_run の詳細な検証については、以下の記事にまとめています。 qiita.com エラートークンの収集と検証バッチの実装 ここからは、エラートークンの収集と検証の方法について紹介します。 テーブル設計 本施策では主に2つのテーブルを使用します。 エラートークンテーブル( error_fcm_tokens ) UNREGISTERED エラーが返されたトークンを記録するテーブルです。FCMトークンそのものをキーとして管理することで、トークンの有効性を直接的に判定できるようにしています。 カラム名 型 説明 fcm_token STRING エラーとなったFCMトークン first_errored_at TIMESTAMP 初めて UNREGISTERED エラーが発生した日時 registered_at TIMESTAMP エラートークンとして登録した日時 再有効化テーブル( reactivated_fcm_tokens ) 一度エラーとなったが、 validate_only による再検証で有効と判定されたトークンの履歴を記録するテーブルです。 カラム名 型 説明 fcm_token STRING 再有効化されたFCMトークン validated_at TIMESTAMP validate_only で検証した日時 reactivated_at TIMESTAMP エラートークンテーブルから削除し再有効化した日時 エラートークンの収集・再検証ワークフロー エラートークンの収集と再検証を日次で行うワークフロー( refresh_error_fcm_tokens )を作成しました。バッチ処理にはワークフローエンジンの Digdag を使用しています。Digdagのワークフロー定義は以下の通りです。Digdagでは + で始まるブロックがタスクを表し、上から順に実行されます。 timezone : Asia/Tokyo schedule : daily> : 00:00:00 # 毎日0時に実行 # ワークフロー全体で使う変数の定義 _export : # 検証結果を格納する一時テーブル名(実行日ごとに一意になるようにする) validated_fcm_tokens_temp_table_id : "project.temp.validated_fcm_tokens_temp_${moment(session_time).format('YYYYMMDD')}" # 並列処理のシャード数 total_shards : 50 # 1. 配信ログからUNREGISTEREDエラーのトークンを収集し、エラートークンテーブルに登録 +collect_fcm_error_tokens : py> : app.collect_fcm_error_tokens # 2. 検証結果を格納する一時テーブルを作成 +create_temp_table : py> : app.refresh_error_fcm_tokens.create_validation_temp_table # 3. エラートークンを50シャードに分割し、並列でFCM APIに検証リクエストを送信 # loop>: 0〜49のインデックス(${i})で繰り返し、_parallel: trueで全シャードを同時実行 +validate_fcm_tokens_parallel : _parallel : true loop> : ${total_shards} _do : +validate_shard : py> : app.refresh_error_fcm_tokens.validate_fcm_tokens_shard shard_index : ${i} total_shards : ${total_shards} # 4. 一時テーブルの検証結果をもとに、有効なトークンをエラートークンテーブルから削除し、 # 再有効化テーブル(reactivated_fcm_tokens)に記録 +update_error_and_reactivated_fcm_tokens : py> : app.refresh_error_fcm_tokens.update_error_and_reactivated_fcm_tokens 以下でそれぞれについて具体的に説明します。 1. エラートークンの収集 はじめに配信ログテーブルから UNREGISTERED エラーのトークンを以下のSQLで収集し、エラートークンテーブルに追加します。このワークフローは日次で実行されますが、対象期間を直近3日間としています。これは、ワークフローが2日連続で失敗した場合でも3日目の実行で未収集分をカバーできるようにするためです。 -- エラートークンの収集クエリ SELECT token AS fcm_token, MIN (delivered_at) AS first_errored_at, CURRENT_TIMESTAMP AS registered_at FROM `project.ma_batch.push_logs` AS push_logs LEFT OUTER JOIN `project.push.error_fcm_tokens` AS target ON push_logs.token = target.fcm_token WHERE status = " FAILED " AND status_detail = " UNREGISTERED " -- 日次実行だが、2日連続WF失敗時でも3日目に回復できるよう3日分のバッファを確保 AND DATE (delivered_at) >= DATE_ADD( CURRENT_DATE ( ' Asia/Tokyo ' ), INTERVAL -3 DAY) AND target.fcm_token IS NULL GROUP BY token 2. 検証用一時テーブルの作成 トークンの有効性の検証結果を格納するための一時テーブルを作成します。各シャードがFCM APIの検証結果をこのテーブルに書き込み、最後にまとめてエラートークンテーブルを更新します。 DROP TABLE IF EXISTS `{validated_fcm_tokens_temp_table_id}`; CREATE TABLE `{validated_fcm_tokens_temp_table_id}` ( fcm_token STRING NOT NULL , -- 検証対象のFCMトークン validated_at TIMESTAMP NOT NULL , -- 検証日時 valid BOOLEAN NOT NULL , -- 有効かどうか error_code STRING, -- 無効だった場合のエラーコード ); 3. エラートークンの再検証(並列処理) エラートークンテーブルに登録済みのトークンに対し、FCMの validate_only APIでトークンの有効性を再検証します。この処理は50シャードに分割して並列実行されます。 検証対象トークンの取得 各シャードが担当するトークンを取得するSQLは以下の通りです。 FARM_FINGERPRINT でトークンをハッシュ化し、シャード数で剰余を取ることで均等に分割しています。また、 first_errored_at が直近30日以内のトークンのみを対象とし、復活の見込みが低い古いトークンへの無駄な検証を避けています。この期間は、調査でわかった UNREGISTERED が続く最大期間の約14日に余裕をもたせて設定しています。 SELECT error_tokens.fcm_token, error_tokens.first_errored_at FROM `project.push.error_fcm_tokens` AS error_tokens LEFT JOIN `{validated_fcm_tokens_temp_table_id}` AS temp_tokens ON error_tokens.fcm_token = temp_tokens.fcm_token WHERE -- 直近30日以内に登録されたエラートークンのみを対象 error_tokens.first_errored_at >= TIMESTAMP_SUB( CURRENT_TIMESTAMP (), INTERVAL 30 DAY) AND -- FARM_FINGERPRINTでトークンをハッシュ化し、シャードに均等分割 MOD ( ABS (FARM_FINGERPRINT(error_tokens.fcm_token)), {total_shards}) = {shard_index} AND -- リトライ時に既に処理済みのトークンを除外 temp_tokens.fcm_token IS NULL シャード単位の検証処理 各シャードでは上記のSQLで取得したエラートークンに対し、FCMの dry_run ( validate_only に対応)でトークンの有効性を検証しています。検証対象のトークン数は数百万件に及ぶため、メモリ効率を考慮して5,000件ごとのバッチに分割して処理しています。検証結果は一時テーブルに書き込まれます。 def validate_fcm_tokens_shard (self, shard_index, total_shards, ...) -> None : # リトライ時に途中から再開できるよう、5,000件ずつ処理する BATCH_SIZE = 5000 # BigQueryからこのシャードが担当するエラートークンを取得 # (例: shard_index=0, total_shards=50 なら、全体の1/50を担当) result = self._bq_client.execute_bigquery_result( query_path= "get_error_fcm_tokens_shard.sql" , params={ "shard_index" : shard_index, "total_shards" : total_shards}, ) fcm_client = FCMClient(fcm_gcp_project) # 5,000件ずつFCM APIで検証し、結果を一時テーブルに書き込む for batch_tokens in self._create_batches(result, BATCH_SIZE): valid_tokens, invalid_tokens = fcm_client.validate_tokens_batch(batch_tokens) self._insert_validation_results(valid_tokens, invalid_tokens) FCM APIによるトークン検証 FCMトークンの実際の検証では、Firebase Admin SDKの messaging.send_each を dry_run=True で呼び出しています。実際にメッセージを配信せずにトークンの有効性のみを検証できます。 send_each は1リクエストあたり最大500件のため、500件単位で分割してリクエストを送信しています。 class FCMClient : BATCH_SIZE = 500 # send_eachの1リクエストあたりの最大件数 def validate_tokens_batch (self, tokens: List[ str ]) -> Tuple[List[ str ], List[Tuple[ str , str ]]]: valid_tokens = [] # 有効と判定されたトークンのリスト invalid_tokens = [] # 無効と判定されたトークンと、そのエラーコードのリスト # 500件ずつに分割してFCM APIにリクエスト for i in range ( 0 , len (tokens), self.BATCH_SIZE): batch = tokens[i:i + self.BATCH_SIZE] # 各トークンに対してダミーのメッセージオブジェクトを生成 messages = [ messaging.Message(token=token, data={ 'validation' : 'true' }) for token in batch ] # dry_run=True により実際の配信は行わず、トークンの有効性のみ検証 batch_response = messaging.send_each(messages, dry_run= True ) # レスポンスからトークンごとの有効/無効を判定 for idx, response in enumerate (batch_response.responses): token = batch[idx] if response.success: valid_tokens.append(token) else : error_code = response.exception.code if response.exception else "Unknown" invalid_tokens.append((token, error_code)) return valid_tokens, invalid_tokens 4. エラートークンテーブルの更新 全シャードの検証が完了した後、一時テーブルの結果をもとにエラートークンテーブルと再有効化テーブルをトランザクション内で一括更新します。有効と判定されたトークンを再有効化テーブルにMERGEし、エラートークンテーブルから削除しています。 BEGIN TRANSACTION; -- 一時テーブルから有効と判定されたトークンを重複排除して抽出 CREATE TEMP TABLE deduped_tokens AS SELECT DISTINCT fcm_token, MAX (validated_at) AS validated_at, CURRENT_TIMESTAMP () AS reactivated_at FROM `{validated_fcm_tokens_temp_table_id}` WHERE valid = TRUE GROUP BY fcm_token; -- 有効なトークンを再有効化テーブルに記録 MERGE `project.push.reactivated_fcm_tokens` AS target USING deduped_tokens AS source ON (target.fcm_token = source.fcm_token AND target.validated_at = source.validated_at) WHEN NOT MATCHED THEN INSERT (fcm_token, validated_at, reactivated_at) VALUES (source.fcm_token, source.validated_at, source.reactivated_at); -- 有効なトークンをエラートークンテーブルから削除 DELETE FROM `project.push.error_fcm_tokens` WHERE fcm_token IN ( SELECT DISTINCT fcm_token FROM `{validated_fcm_tokens_temp_table_id}` WHERE valid = TRUE ); COMMIT TRANSACTION; パフォーマンス 上記の処理がどれくらいの時間で完了するのか、パフォーマンス計測をした結果は以下の通りです。 対象件数 並列数 処理時間 10万件 1並列 約25分 約800万件(全量) 50並列 約50分 また、FCM APIのQuotaについても確認し、日中に実行しても問題ない余裕があることを確認しました。 既存の全トークンの再検証と本番リリース 初回実行:全期間のエラートークンを検証 初回実行では、過去に蓄積された全エラートークン(約754万件)を対象に検証しました。通常運用では直近30日以内のエラートークンのみを検証対象としていますが、初回は既存の全トークンの検証が必要でした。そのため、検証対象を取得するSQLの30日の条件を一時的にコメントアウトしてワークフローを実行しました。 WHERE -- 初回実行時は全期間のエラートークンを対象にするため一時的にコメントアウト -- error_tokens.first_errored_at >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY) AND MOD ( ABS (FARM_FINGERPRINT(error_tokens.fcm_token)), {total_shards}) = {shard_index} AND temp_tokens.fcm_token IS NULL 実行前のエラートークン数:約7,500,000件 エラートークン収集直後:約+70件(新規エラートークン) 再検証後:約ー170件(復活トークン) 約170件のトークンが validate_only で有効と判定され、エラートークンから解除されました。 通常運用の開始 初回実行後、1か月以内に登録されたエラートークンを対象とする通常運用を開始しました。 実行前のエラートークン数:約7,500,000件 エラートークン収集直後:約+6,500件(新規エラートークン) 再検証後:約ー10件(復活トークン) 日次で約10件のトークンが再有効化されていることが確認できました。 まとめ 本記事では、FCMエラートークンの管理を精緻化した取り組みについて紹介しました。 従来は UNREGISTERED エラーの返されたトークンを即時かつ永続的にエラー扱いとしていました。しかし調査の結果、一度無効になったトークンが復活するケースの存在を確認しました。そこでFCMの validate_only APIを活用した定期的な再検証の仕組みを導入し、復活したトークンを自動的にエラーリストから解除するようにしました。 この改善により、以下の効果が得られました。 無効トークンへの無駄な配信リクエストの削減によるコスト最適化 セグメントのボリューム把握の精度向上 トークン復活時の配信漏れ防止 FCMトークンの管理は、Push配信の品質とコストに直結する重要な要素です。同様の課題をお持ちの方の参考になれば幸いです。 最後に ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
こんにちは、 電通 総研 XI本部 サイバーセキュリティテクノロ ジー センターの櫻井です。 本記事では 代表的な クラウド サービスプロバイダ(以降、CSPと略)である AWS ( Amazon Web Services )、 Microsoft Azure、 Google Cloudの最上位アーキテクト資格に関する比較を紹介します。 なお、本記事でご紹介する資格の情報は2026年1月時点のものとなります。 アーキテクト資格の種類 筆者の経歴 チャレンジのおススメ順 各CSP最上位資格で共通で問われる内容 各CSP最上位資格の特色 まとめ 最後に アーキテクト資格の種類 各CSP資格群はFoundational、Associate、Professiona(Expert)lの3段階に分かれており、受験者の対象と試験の大枠としては以下を想定しています。 Foundational 対象: クラウド 初心者やIT未経験者。また、基礎知識を理解したい営業・ マーケティング ・プロジェクトマネジメントサイドの担当者 試験の大枠: クラウド 自体の理解、大枠のサービス概要、 クラウド 利用料計算の理解 Associate 対象:ソリューション設計に1年以上の実務経験のあるITエンジニア 試験の大枠:設計に用いるソリューションの基本理解 Professional(Expert) 対象:ソリューション設計に理解のある、実務経験のあるITエンジニア 試験の大枠:設計時のソリューションベストプラクティスの理解、適切なサービス組み合わせの理解 それぞれの資格群の名称(略称)に関しては以下の表のとおりです。 Grade AWS Azure Google Foundational Cloud Practitioner(CLF) Azure Fundamentals(AZ-900) Cloud Digital Leader(CDL) Associate Solutions Architect Associate(SAA) Azure Administrator(AZ-104) Associate CloudEngineer(ACE) Professional(Expert) Solutions Architect Professional(SAP) Azure SolutionsArchitect Expert(AZ-305) Professional Cloud Architect (PCA) 以降は、Professional、Expert資格にフォーカスして説明を行います。 筆者の経歴 (テンプレにはなりますが、)オンプレ& クラウド 、ネットワーク&セキュリティのごちゃまぜエンジニアです。担務領域に関しては特にこだわりなくなんとなく興味を持った資格は取得してます。(※ My credly ) 電通 総研ではサイバーセキュリティテクノロ ジー センターで主にセキュリティサービス設計、運用やマルチ クラウド のセキュリティ設計レビューに従事しています。 また、筆者の各CSPの経験歴としては以下となります。 AWS 7年程度、メインは クラウド インフラ設計ガッツリです。オンプレ接続のために用いる クラウド リソース管理や運用/監視、リソースデプロイの自動化がメインタスクでした。 電通 総研に入社後はアプリまで含めた AWS 全般のセキュリティ設計レビューを行っています。 Microsoft Azure 5年程度、メインは クラウド マイグレーション に関わるインフラ設計、ID( Microsoft Entra)の設計・運用でした。 電通 総研に入社後は Microsoft defender for cloudをはじめとしたセキュリティサービスの調査や社内向けサービス設計に従事しています。 Google Cloud 3年程度、以前はBigQueryやApp Engineを触っていた程度です。 電通 総研に入社後は Google Cloudログサービスの調査・検証の実施、Firebaseを利用したWebサイト構築のセキュリティ設計レビューにも参加しています。 チャレンジのおススメ順 最初に、3種すべてを取得するならばの前提で筆者の考える難易度を記載します。各試験で問われる範囲と出題形式、傾向、学習コンテンツを踏まえて、以下の順が望ましいと考えています。 Google Cloud(Professional Cloud Architect) AWS (Solutions Architect Professional) Microsoft Azure(Azure Solutions Architect Expert) はい、見事に筆者のチャレンジ順とは逆です。 単一CSPでガッツリ設計をやっている方は専門の分野を突き詰めるのが望ましいのであまり難易度比較に意味はありませんが、基本は下に行くほど難しい試験である(事前準備が必要である)と筆者は考えています。 各CSP最上位資格で共通で問われる内容 最上位資格の試験について、共通で必要とされる内容を簡単に整理します。 対象CSPのサービス全般に対する理解① 対象のサービス種類(試験に含まれるサービスの範囲) CSPの試験ガイドで具体的に対象と記載されているサービスについてすべてが対象となります。 例として、 AWS (Solutions Architect Professional)であれば 試験ガイド の「範囲内の AWS のサービスと機能」となっているサービスはすべて対象です。 各試験によってどこまでを対象とするかは異なりますが、ITサービス構築のために クラウド インフラを利用するだけではなじみのないAIやデータ分析のサービスも含まれますので、普段利用していないサービスでも用途等は理解しておく必要があります。 対象CSPのサービス全般に対する理解② サービスの単位 「 AWS における VPC はリージョナルサービスである」、「 Google Cloudにおける VPC はグロー バルサ ービスである」という比較に代表されるように クラウド 設計におけるサービスがどこまでの範囲で展開可能かを明確に覚えておきましょう。 対象としているCSPにフォーカスしてきちんと学習しましょう。実務目線でも設計や提案の際に理解できていないと致命的です。 例えば、複数リージョンに渡る負荷分散の設計方法は各CSP毎に大きく異なる等ですね。 (この場合、グローバルロードバランシングの種類をちゃんと理解しているか?になります) 設計の理解① 設定パラメータ 利用ケースが多いサービス(コンピュート、コンテナ、アプリケーション統合)については細かい設定が問われる問題が出題されます。この点に関しては明確に実務経験があった方が望ましいといえます。 対象サービスのパラメータを設定するのに、どういった方法があるのかも含めて押さえておくとよいです。 1.「環境を作りなおす必要があるのか?」もしくは「既存の環境のままで設定変更が可能か?」 2.「マネジメントコンソールから設定できるか?」もしくは「 API や CLI からのみ設定できるのか?」 設計の理解② ソリューションの選定、ソリューションの組み合わせ比較 出題内容としては、具体的なソリューションを一意に特定するケースに加えて、必要な要件に対して複数のソリューションの組み合わせを比較した上で最も適したものを選択するケースも存在します。 いわゆるベストプラクティスを選択する必要がありますので、 AWS の場合は Black Belt の様な公式のソリューションガイドを参考に学習するとよいです。 筆者の経験でも、顧客の特殊要件を踏まえ意図的にベストプラクティスを選択しなかった経験もありますし、当時と比較して新しいサービスがリリースされている可能性もありますので、最低限は確認しましょう。 各CSP最上位資格の特色 それぞれの試験の概要と筆者の試験自体に関する印象、筆者の主観的分析を紹介します。 AWS Solutions Architect Professional(SAP) 問題数:75問、時間:180分 試験後の印象 とにかく問題数が多い。そして問題文、選択肢の文章が比較的長いので持続的な集中力を求められる。 AWS のProfessional、Security分野の試験では共通している点かと思います。 主観的分析 AWS に関してはCSPがリリースしているサービスの種類が他の2社と比べて多いです。(200個以上) 時間をかけて理解する必要があり、初学者は数に圧倒されると思います。 問題そのものは取り組みやすいですが、文章量の理解に時間を要するため練習問題等で慣れておく必要があります。 SAPを含め、 AWS 資格のコンテンツは数多く存在するためこの点は学習者にプラスです。 Azure Solutions Architect Expert(AZ-305) 問題数: 不定 (概ね50問前後)、時間:120分 試験後の印象 設計の組み合わせやサービスの詳細など細かい部分まで回答を求められる。(理由は後述) 試験への慣れが必要。3パートに分かれており、2パートは選択を終了したら戻ることができない。 本試験はダイレクトに「 クラウド インフラ設計」の理解を求められる。 Microsoft Webページ の記載の通り、複数系統に分かれているためです。 主観的分析 CSP3種試験の中では難易度は最も高いと思います。 設計にフォーカスしているため、サービス単体を理解しているだけではNGなケースが多いです。 Microsoft AzureのAssociate以上の試験に関しては Microsoft Learn でコンテンツを検索しながら回答が可能です。 その点に起因するかはわかりませんが、事前の学習では手が回らないような調べないとほぼ回答できないような細かいサービス詳細に関する問題も一部出題されます。(この要件を満たすライセンスの種類は?というような問題) 購入した問題集の設問で構わないのでこんな感じで検索したら出てきそうというのを Microsoft Learnで事前に確認しておくとよいです。関係のないコンテンツも検索結果に含まれてしまい、思いのほか抽出が難しいです。 40問程度の大問パート以外にシナリオ問題とケース問題で合計3パート以上で構成され、パート間で戻ることはできないため、時間配分を間違わないようにしましょう。(筆者はAZ-305以外の試験で勘違いし、ドボンしています) 市販の学習用コンテンツは最低限は存在します。 Microsoft Azureは Microsoft Learn以外にも公式の トレーニングコース が充実しているため、集中的に学習したいものは登録しましょう。 開催日程が決まっていますが、 Microsoft Virtual Training Days で無料で受講できるハンズオンや講習も存在するので確認しましょう。 Google Cloud Professional Cloud Architect(PCA) 問題数: 不定 (50問以上、60問未満)、時間:120分 試験後の印象 一定以上の知識は必要だが、複雑な問題はあまり出題されない。 サービスの種類と設計をバランスよく理解しておくことが求められる。 数問で構成されるケース問題が複数出題される。全く関係ない導入文も含まれるので選択をミスらないようにしましょう。 試験中に問題言語を英語に切り替えられない。筆者だけかもしれませんが、細かいオプション説明を英字で確認しようと切り替えても日本語のままでした。 主観的分析 個人的な忖度ではないですが、試験自体は一番取り組みやすいと感じました。試験時間と問題文、文章量がマッチしていると思います。 ACEとは別にProfessionalとなっているので簡単ではないですが、適度な難易度の問題が適量出題されるイメージです。 難点は学習コンテンツが少ないことかと思います。PCAに限っては市販の書籍は見当たりませんでした。 Udemyのコンテンツで日本語対応のもの(※1、※2)がありますのでこちらをお勧めします。 筆者の感覚では既にACEを取得済みであれば、Udemyのコンテンツを繰り返すだけでも合格することは十分可能だと思います。(自信がない方はラボ演習をしましょう) 所属する会社によって変わりますが、 Google Cloudに関する招待制ト レーニン グプログラムも存在します。無料でラボコンテンツが一定期間利用できるプログラムです。 ※1 https://www.udemy.com/course/google-cloud-professional-cloud-architectpca/ ※2 https://www.udemy.com/course/google-cloud-professional-cloud-architect-i/ まとめ 以上を踏まえて、それぞれの試験を試験自体の難易度(筆者の感覚)、試験への慣れの必要性、学習コンテンツの豊富さで整理したいと思います。(勉強時間は各個人によるので含めません) 評価観点 AWS (SAP) Azure(AZ-305) Google (PCA) 試験自体の難易度(筆者感覚) 普通 (サービスの数は多いが基本的なサービス組み合わせ、設計等を理解できていれば問題なし。) 難しい (ひとえに難しい。細かいところまで出題される。) 比較的取り組みやすい (サービスの種類を覚え、組み合わせや設計が理解できていれば問題なし。) 試験への慣れの必要性 ある程度必要 (問題数、文章量への慣れが必要。) 慣れが必要 (最終的にExpertとなるためにはAZ-104の合格も併せて必要なので先に受験し訓練しましょう。) ほぼ不要 (初チャレンジでもケース問題の切り替えに注意すれば問題はないと思います。) 学習コンテンツの豊富さ 豊富 (ググっても、 Amazon で検索しても、Udemyでも大量にあります。むしろ精査が難しいレベル。) 普通 (発売されているものは少ないですが、良書が多いので一度は目を通しましょう。) 少ない (市販書籍がないのでUdemy等を活用しましょう。) 表からチャレンジ順の理由がご理解いただけたかと思いますが、AZ-305に関しては、事前に準備しないといけないもの(知識も当然だが、問題形式や回答方法の予習)が多く、3種横並びで見た場合で最初にチャレンジするのは難易度が高いという評価です。ただ、 クラウド インフラ設計という点からは逸脱している内容ではないので、学んでおくことでプラスになる内容であると思います。 最後に 読了いただきありがとうございました。 筆者は事前の積み重ねもあって3種のArchitect最上位資格を約半年間で取得できました。しかし、エンジニア(もしくは コンサルタント )としてのゴールは資格を取得することではありません。 基本は クラウド への理解を深めるためにCSPの提供するラボ等を活用し、じっくり学習することをお勧めします。 今後も クラウド 、セキュリティに関する投稿を行っていきますのでよろしくお願いします。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @sakurai.ryo レビュー: @kobayashi.hinami ( Shodo で執筆されました )
Flutter3.38アップグレードにおけるiOSとAndroidの影響範囲 背景 Flutter3.38アップグレードの手順 パッケージのバージョン依存関係調整 依存関係の解消によって副次的問題が発生 1. Dart SDK バージョン 2. Ferryエコシステム(GraphQLクライアント) 3. Freezed(コード生成) 4. Firebase(iOS 13対応のため2.x系を使用) 5. その他の重要な更新 6. Isar Plus(データベース)モデル修正 解決した依存関係の競合 競合1: isar_db_generator 競合2: intl バージョン 競合3: source_gen バージョン 競合4: build バージョン 競合5: gql_exec バージョン 競合6: ferry バージョン 競合7: web パッケージ(iOS 13対応のための調整) 競合8: Firebase iOS 15要件 参考リンク 既存コードの改修作業 1. isar_plus 移行に伴うimport文の変更 2. isar_plus 移行に伴うPodfileの変更 3. NDKバージョンの明示的指定 4. Android app の namespace(AGP 8.x 必須) 5. サブプロジェクトへの namespace 注入 6. BuildConfig の有効化 7. Freezed 3.x マイグレーション(全モデルに abstract 修飾子) 7.1. 7のLintエラー対応 8. retrofit / ParseErrorLogger 対応 9. Android 旧埋め込み (PluginRegistry.Registrar) 削除対応 10. iOS CocoaPods更新 11. dart formatの修正 Flutter バージョン別 カート追加時パフォーマンス比較レポート 比較サマリー 結論 主な改善要因(推測) 今後の検討 まとめ 1. テスト 2. iOS 11-12ユーザーへの対応検討 最後に  こんにちは、開発本部 開発2部 RetailHUB NetSuperグループに所属するホーク🦅アイ👁️です。 背景  現在提供しているネットスーパーアプリはFlutter+Dartで実装しております。一方で、昨年2025年11月1日までに対応が必要であったGoogleからの メモリの16KBページサイズをサポートせよ という通知を延長申請して2026年5月31日まで対応を保留にしていました。2026年2月現在、この延長期限も近づいてきていることを理由にFlutterのバージョンを3.38系にアップグレードすることになりました( こちら の公式ブログにも3.38から標準対応したとあります)。 Flutter3.38アップグレードの手順  通常、Flutterのバージョンをアップグレードする際の流れとしては、以下の2点を気にする必要があり実際にそのプロセスを踏まないといけないことも少なからずあります。 パッケージ依存関係の解消 既存ソースコードで改修  今回、対象のソースコードで利用しているFlutterバージョンは3.24.1と古めのバージョンであるためバージョン差分が大きく、2点とも対応が発生しました。以下にその2点の対応について詳細を記していきます。 パッケージのバージョン依存関係調整  目的はあくまでAndroidアプリの16KBページサイズに対応することなのでAndroidアプリのビルドをまず意識した以下のバージョンを調整しました。 Flutter,Dartのバージョンを上げる Flutter3.38.9、Dart3.10.8にしました DevTools 2.51.1 Gradleのバージョンを上げる 8.7にしました AGPのバージョンを上げる 8.5.1にしました Kotlinのバージョンを上げる 2.0.21にしました NDKバージョンを明示指定 29.0.14206865にしました その他利用ライブラリの対応バージョンをtrial and errorで上げる 現状のバージョンのままでビルドしてみてエラーが出たら上げるを繰り返しました 依存関係の解消によって副次的問題が発生  今回、Androidアプリのためにアップグレードをするので必要なライブラリパッケージも必要最低限のものだけを最小限でアップグレードすることを心がけたのですが、意図せずiPhoneアプリ側の方にも影響を及ぼしてしまうことが判明しました。具体的には以下のことが発生しました。 iOSの最小メジャーバージョン番号が12から13以上に引き上げ  直接的な引き上げ条件は、 Flutter公式 によれば、swiftコードを利用する場合とありますがFlutter3.38自体がそれに該当するということ( 参考ページ )でした。というわけで、必然的にFlutter3.38にしないといけない場合はiOS最小対応バージョンも13以上になるということでした。  最終的には以下に挙げるバージョン対応で一旦、すべての依存関係の競合を解決し、 pub get 、コード生成、iOS CocoaPodsインストールがすべて成功しました。 1. Dart SDK バージョン 変更 : >=3.5.0 <4.0.0 → >=3.10.0 <4.0.0 理由 : json_serializable と freezed の最新版が要求 2. Ferryエコシステム(GraphQLクライアント) パッケージ 最終バージョン 理由 ferry ^0.16.1 ferry_generator 0.12.0+3との互換性 ferry_generator ^0.12.0+3 build 4.0対応 build_runner ^2.10.3 build 4.0対応 gql_code_builder_serializers ^0.1.0 ferry_generator 0.12.0+で必須 gql_exec ^1.0.0 ferry_generator依存関係 gql_http_link ^1.0.0 gql_execとの互換性 gql_transform_link ^1.0.0 gql_execとの互換性 3. Freezed(コード生成) パッケージ 最終バージョン 理由 freezed ^3.2.5 build 4.0対応 freezed_annotation ^3.0.0 freezed 3.x対応 4. Firebase(iOS 13対応のため2.x系を使用) パッケージ 最終バージョン 理由 firebase_core ^2.32.0 iOS 13サポート(3.x+はiOS 13必須) firebase_analytics ^10.10.7 firebase_core 2.x互換 firebase_crashlytics ^3.5.7 firebase_core 2.x互換 firebase_messaging ^14.7.10 firebase_core 2.x互換 & webパッケージ互換 firebase_remote_config ^4.4.7 firebase_core 2.x互換 firebase_app_installations ^0.2.5+7 firebase_core 2.x互換 Firebase iOS SDK : 10.25.0(iOS 13+をサポート) 5. その他の重要な更新 パッケージ 最終バージョン 理由 intl ^0.20.2 flutter_localizations要求 retrofit_generator ^10.2.1 source_gen 4.0対応 json_annotation ^4.9.0 json_serializable要求(dependenciesに追加) 6. Isar Plus(データベース)モデル修正 isar_plus v4のAPI変更に対応: 変更後(isar_plus v4スタイル): @collection class EventLog { EventLog({required this .id}); final int id; // Auto-increment id (isar_plus v4) late String data; } 主な変更点: @Collection() (大文字)→ @collection (小文字) Id id = Isar.autoIncrement → final int id とコンストラクタで受け取る 実際のauto-increment IDは isar.collection.autoIncrement() で生成 解決した依存関係の競合 競合1: isar_db_generator 問題 : isar_db_generator パッケージが存在しない 解決 : isar_db は元の isar_generator を使用することを確認 競合2: intl バージョン 問題 : flutter_localizations が intl 0.20.2 を要求 解決 : intl を ^0.20.2 に更新 競合3: source_gen バージョン 問題 : isar_plus が source_gen ^4.0.2 を要求、 retrofit_generator ^8.1.0 が source_gen ^1.3.0 を要求 解決 : retrofit_generator を ^10.2.1 に更新(source_gen 4.0対応版) 競合4: build バージョン 問題 : build_runner >=2.9.0 が build ^4.0.0 を要求、 freezed ^2.x が build ^2.3.1 を要求 解決 : freezed を ^3.2.5 に更新(build 4.0対応版) 競合5: gql_exec バージョン 問題 : gql_transform_link が古い gql_exec を要求、 ferry_generator 0.12.0+3 が gql_exec ^1.0.0 を要求 解決 : gql_exec 、 gql_http_link 、 gql_transform_link をすべて1.x系に更新 競合6: ferry バージョン 問題 : ferry ^0.14.2+1 が ferry_exec ^0.3.1 を要求、 ferry_generator 0.12.0+3 が ferry_exec ^0.7.0 を要求 解決 : ferry を ^0.16.1 に更新 競合7: web パッケージ(iOS 13対応のための調整) 問題 : isar_plus が web ^1.1.0 を要求、Firebase 4.x系が web ^0.5.1 を要求 解決 : Firebaseパッケージを2.x系にダウングレード(iOS 13サポートのため) 競合8: Firebase iOS 15要件 問題 : Firebase 4.x系(firebase_core 4.0+)はiOS 15を最小要件とする 解決 : Firebase 2.x系(firebase_core 2.32.0)を使用してiOS 13をサポート 参考リンク isar_plus v4 ドキュメント ferry_generator changelog freezed 3.0 migration Firebase Flutter changelog firebase_core 3.0.0 breaking changes 既存コードの改修作業  前述にある依存パッケージライブラリをアップグレードすることで破壊的変更が発生してしまった全箇所をエラーがなくなるまで対応していくという作業も相当数発生しました。 1. isar_plus 移行に伴うimport文の変更   package:isar/isar.dart → package:isar_plus/isar_plus.dart に変更 2. isar_plus 移行に伴うPodfileの変更 ios/Podfile で isar_flutter_libs → isar_plus_flutter_libs に変更 3. NDKバージョンの明示的指定 追加 : ndkVersion "29.0.14206865" (安定版最新。r28+ で 16KB アライメント対応のため r29 を使用) ファイル : app/build.gradle 理由 : NDK r28以上で16KBアライメントがデフォルト対応。固定していた 28.0.12674087 ではなく、安定版最新 29.0.14206865 を推奨 4. Android app の namespace(AGP 8.x 必須) 追加 : namespace "tv.every.fresh" ファイル : app/build.gradle 理由 : AGP 8.x ではモジュールに namespace の指定が必須。未指定だと「Namespace not specified」でビルド失敗する。 5. サブプロジェクトへの namespace 注入 追加 : Android library プラグインで namespace 未指定のサブプロジェクトに、 AndroidManifest.xml の package を namespace として設定 ファイル : build.gradle (root) 理由 : AGP 8.x では全モジュールに namespace 必須。古いパッケージは namespace 未指定のため「Namespace not specified」でビルド失敗する。pub cache は編集しないため、root の subprojects.plugins.withId("com.android.library") で manifest の package を注入する。 6. BuildConfig の有効化 追加 : 全 Android サブプロジェクトで buildFeatures.buildConfig true ファイル : build.gradle (root) 理由 : AGP 8.x では BuildConfig がデフォルト無効。custom BuildConfig を使うパッケージがあったため「defaultConfig contains custom BuildConfig fields, but the feature is disabled」でビルド失敗する。root の subprojects.afterEvaluate で全モジュールに有効化する。 7. Freezed 3.x マイグレーション(全モデルに abstract 修飾子) 対象 : @freezed を付けた全てのモデルクラス 対応 : Freezed 3.0 マイグレーションガイドに従い、 全てのクラス定義に abstract 修飾子を追加 変更例 : // 変更前 @freezed class Shop with _$Shop {...} // 変更後 @freezed abstract class Shop with _$Shop {...} 理由 : Dart 3.10 コンパイラ下で non_abstract_class_inherits_abstract_member エラーを解消するため。Freezed 3.x の生成コード(mixin の抽象メンバー)と互換させるには、公開クラスを abstract class にすることが必要。 実施方法 : 上記のとおり、該当する全モデルファイルで class → abstract class に手動で置換。 検証 : 本対応後に iOSビルド成功し、iOS Simulator(26.2)でアプリ起動を確認済。 このマイグレーションでLintエラー問題が発生したので以下に経緯と対応を追記しておきます。 最初の実装でLintエラーが発生していたので「 @JsonSerializable(...) を factory constructor からクラスレベルに移動」しましたが、この変更により build_runnerが失敗したため、この修正では不十分であることが判明しました。 @JsonSerializable をクラスレベル(abstract class)に置くと、 json_serializable ジェネレーターは、以下の動きをします。 abstract class のコンストラクターを探す const Xxx._() (private constructor)しか見つからない、またはコンストラクターなしと判断 Freezed が生成した factory constructor( = _Xxx )との対応を解決できない 「required constructor argument を populate できない」エラーになる Freezed + json_serializable の正しいパターンでは、 @JsonSerializable は factory constructor に置く必要がありました 。Freezed はこのパターンを前提に .g.dart ファイルを生成する必要がありました。 7.1. 7のLintエラー対応 @JsonSerializable は factory constructor に残したまま 、 // ignore: invalid_annotation_target を @JsonSerializable の 直前の行 に追加しました。 ポイント: // ignore: <rule> は 次の1行 に適用されます。旧コードでは ignore コメントがクラス宣言行にあったため、実際に警告が出る @JsonSerializable 行をカバーできていませんでした。 8. retrofit / ParseErrorLogger 対応 対象 : retrofit パッケージをimportしているREST APIを実装したdartファイル(ex. rest_api.dart) 現象 : 旧 retrofit 4.1.0 + retrofit_generator 10.2.1 で生成したコードが型 ParseErrorLogger を参照するが、 package:retrofit/http.dart のみの import では参照できずコンパイルエラーになる 対応 : retrofit : ^4.1.0 → ^4.9.2 に更新(Dart 3.8 対応バージョン、ParseErrorLogger は package:retrofit/retrofit.dart で提供) rest_api.dart : import 'package:retrofit/retrofit.dart'; を追加し、生成コード(part ファイル)から ParseErrorLogger を参照可能にする。 package:retrofit/http.dart は retrofit.dart に含まれるため削除可 結果 : build_runner 再生成後も手動修正不要でビルド可能 9. Android 旧埋め込み (PluginRegistry.Registrar) 削除対応 Flutter 3.38 では v1 Android 埋め込み API( PluginRegistry.Registrar / registerWith )が削除されている。以下のパッケージを更新済み。 path_provider : ^2.1.3 → ^2.1.5 (path_provider_android 2.2.5+ を要求し、v1 削除済み) shared_preferences : ^2.2.3 → ^2.3.4 (shared_preferences_android 2.2.3+ で v1 削除済み) url_launcher_android : ^6.0.38 → >=6.3.3 <6.3.27 (6.3.3 で v1 削除。6.3.27+ は androidx.browser 1.9.0 が AGP 8.9.1 を要求するため 6.3.26 以下に制限) compileSdkVersion : 34 → 36(path_provider_android 等が SDK 36 を要求。 app/build.gradle ) 10. iOS CocoaPods更新 iOS最小デプロイメントターゲットをiOS 13.0にFixしました。 platform :ios , ' 13.0 ' config.build_settings[ ' IPHONEOS_DEPLOYMENT_TARGET ' ] = ' 13.0 ' 11. dart formatの修正 Dart SDK が 3.5→3.10 に上がったことで、同梱の dart_style が 2.3.x→3.x に major version upgrade されました。dart_style 3.x では "tall style" と呼ばれる新しいフォーマット戦略が導入され、trailing comma の扱いや multi-line expression のレイアウトルールが根本的に変わったため、コードに問題がなくても 100 ファイル以上が再フォーマット対象となり、大量のフォーマット修正を余儀なくされました。 Flutter バージョン別 カート追加時パフォーマンス比較レポート  新しいバージョンになったのでそれだけでどれだけ既存アプリのパフォーマンスにも影響を及ぼしたか気になったのでFlutter DevToolsでプロファイリングしてTimeline Eventsを3.24.1と3.38.9でベンチマーク比較してみました。  以下は、 同じカート追加アクション で取得したDevToolsの計測データを用い、 Flutter 3.24.1 と Flutter 3.38.9 のパフォーマンスを比較したレポートです。 項目 Flutter 3.24.1 Flutter 3.38.9 総フレーム数 96 107 比較サマリー 指標 Flutter 3.24.1 Flutter 3.38.9 差分 傾向 平均FPS 19.8 fps 48.3 fps +28.5 fps ✅ 大幅改善 平均フレーム時間 50.47 ms 20.71 ms -29.76 ms ✅ 約59%短縮 平均ビルド時間 7.23 ms 1.25 ms -5.98 ms ✅ 約83%短縮 平均ラスター時間 26.86 ms 15.32 ms -11.54 ms ✅ 約43%短縮 平均Vsyncオーバーヘッド 5.96 ms 1.46 ms -4.50 ms ✅ 約75%短縮 最大フレーム時間 274.89 ms 85.51 ms -189.38 ms ✅ 約69%短縮 最大ビルド時間 115.16 ms 18.66 ms -96.50 ms ✅ 約84%短縮 最大ラスター時間 145.56 ms 41.46 ms -104.10 ms ✅ 約72%短縮 Janky率 100.0% 88.8% -11.2pt ✅ 改善 重度Jank率 (>33ms) 38.5% 4.7% -33.8pt ✅ 大幅改善 結論 Flutter 3.38.9 は 3.24.1 と比較して、カート追加時のパフォーマンスが全体的に大きく改善しています。 平均FPSが 19.8 → 48.3 と約2.4倍になり、体感の滑らかさが向上しています。 ビルド時間・ラスター時間・Vsyncオーバーヘッドのいずれも短縮。 最大フレーム時間は 274.89ms → 85.51ms(約69%短縮) と、改善が確認できます。 重度Jank率は 38.5% → 4.7% と約1/8に減少。 主な改善要因(推測) Flutterエンジン・Skiaの最適化 ビルドパイプラインの効率化(ビルド時間の大幅短縮) Vsyncまわりのオーバーヘッド低減 今後の検討 3.38.9 時点でも Janky率 88.8%、平均FPS 48.3 であり、60fps目標にはまだ余裕があります。 ラスターが主なボトルネックのため、画像最適化・RepaintBoundary・Clip削減などの施策を続けると、さらに改善の余地があります。 まとめ  本ブログでは、Android15以降でサポートされている16KBページサイズに対応するためFlutterのバージョンを3.38系にアップグレードすると既存アプリにどのような影響を及ぼすことになるかについてお話ししました。  一旦は、両OSともビルドが成功してアプリ起動までは確認が取れたのでこれから5月31日まであまり日がないですが以下のようなThe next stepsに基づいて進めていく予定であることをお知らせして結びとさせていただきます。 1. テスト 単体テストの実行 統合テストの実行 手動テスト(特にデータベース操作とGraphQL操作) 2. iOS 11-12ユーザーへの対応検討 アプリストアで古いバージョンを継続提供 ユーザーへの事前通知 段階的な移行計画 最後に エブリーでは、ともに働く仲間を募集しています。 テックブログを読んで少しでもエブリーに興味を持っていただけた方は、ぜひ一度カジュアル面談にお越しください! corp.every.tv

動画

書籍