CodexでFlutterアプリを3か月未満で公開したけど、今どきの個人開発はコード以外が大変すぎる
OpenAIのCodexを試すつもりでFlutterアプリを作り始めたら、気づいたら87日でGoogle Play公開まで到達していました。
しかし一番衝撃だったのは、コードを書く時間よりも、公開するための周辺作業の方が圧倒的に大変だったということです。
前回の記事では、Codex を試すつもりで Flutter アプリを作り始めたら、約2カ月で Google Play のクローズドテストまで到達していた、という話を書きました。
その後、Google OAuth の審査、クローズドテストの参加者集め、家族からのフィードバック対応、本番環境の利用申請、製品申請を経て、最終的に Google Play で製品公開まで到達しました。
ファーストコミットは 2026年2月28日。
製品公開日は 2026年5月26日。
日数でいうと 87日です。
流れとしては、ファーストコミットから約2カ月でクローズドテストまで到達し、そこから1カ月かからずに、テスター集め、Google OAuth 審査、簡易編集機能の追加、本番申請、製品公開まで一気に進めた形です。
つまり、ファーストコミットから3か月未満で Google Play 公開まで到達できたことになります。
Codex を使うことで、実装速度はかなり上がりました。
ただ、今回やってみて強く感じたのは、そこだけではありません。
むしろ一番感じたのは、
今どきの個人開発は、コードを書く以上に、公開するための周辺作業が多すぎる
ということでした。
以前作った Windows 向けの テキストエディタ gPad を作っていた頃は、公開までの流れはもっとシンプルでした。
- アプリを作る
- インストーラーを作る
- 公式サイトを作る
- ダウンロードリンクを置く
基本的には、それで配布できました。
でも、今のスマホアプリ、特に Google Play で公開するアプリはまったく違いました。
アプリを作ったあとに、やることが大量にあります。
-
利用規約の作成
-
プライバシーポリシーの作成
-
公式サイトの作成
-
Google Play Console 対応
- すべての機能を Google Play の審査が行える状態にする
- GCS / S3 の審査向け準備
- アプリの説明文
- スクリーンショット
-
Google OAuth 審査
-
クローズドテスト
- テスター集め
-
本番環境の利用申請
-
製品版申請
コードを書き終わっても、そこから公開に向けた詰め作業が始まります。
この記事では、Codex で Flutter アプリを作って Google Play 公開まで到達した話を書きつつ、今どきの個人開発で「公開する」ことの大変さについて書いていきます。
作っているもの
作っているのは Flutter 製のギャラリーアプリです。
スマホ内の写真を見るだけではなく、クラウド連携を前提にしたギャラリーアプリです。
主な特徴は以下です。
-
Flutter 製
-
Android アプリ
-
S3 対応
-
GCS 対応
-
Google OAuth 対応
-
クラウド上の画像閲覧
-
クラウドへのバックアップ
-
簡易編集機能
- 文字入れ
- 円や矢印などの図形配置
- モザイク加工
- 簡単な切り取り

簡易編集機能の画面例(文字入れ・モザイク・図形配置)
もともとは「Codex を試してみよう」ぐらいの気持ちで始めました。
それが、気づけば約2カ月でクローズドテストまで行き、3か月未満で Google Play の製品公開まで到達していました。
AI を使えば、個人でもかなりの速度でアプリを作れる。
これは今回、かなり実感しました。
ただ、昔に比べると、アプリを公開するための大変さも強く実感しました。
Google OAuth 審査は、思ったよりちゃんと説明しないと通らない
クローズドテストを始めたのと同じタイミングで、Google Cloud Storage 連携に Google ログインを利用するため、Google OAuth の審査も進めました。
ここでいくつか詰まりました。
ホームページの自動判定で落ちる
最初に出たエラーはこれです。
ホームページにプライバシーポリシーへのリンクが掲載されていません。

ちゃんとホームページ上にはプライバシーポリシーへのリンクを置いていました。
ただ、ホームページには Cloudflare Pages を使っていました。
さらに、Bot Fight Mode や海外アクセス制限なども有効にしていました。
おそらく Google 側の自動判定 Bot が正常にアクセスできていなかったのだと思います。
そこで、Cloudflare 側の設定を調整しました。
- Bot 関連の防御を緩める
- 海外アクセス制限を調整する
- Google の審査 Bot がアクセスできそうな状態にする
この対応後、ホームページの自動判定は通過しました。
個人開発で Cloudflare Pages を使っている人は多いと思います。
ただ、Google OAuth 審査や Play Console 周りでは、Bot 対策やアクセス制限が逆に審査の邪魔になることがあるようです。
セキュリティを強くすること自体は良いことですが、審査 Bot が見られない状態になると、当然ながら審査には通りません。
これは実際にやってみるまで気づきませんでした。
YouTube で説明動画を作る必要があった
OAuth 審査では、アプリがどのように Google の権限を使うのかを説明する動画が必要でした。
そこで、YouTube に限定公開で説明動画をアップロードしました。
ただ、最初に作った動画は一度差し戻されました。
理由は、動画内で権限の詳細をタップしている様子が確認できない、というものでした。
つまり、単にアプリにログインして、機能を使う様子を撮るだけでは足りませんでした。
Google のレビュアーが確認したいのは、おそらく以下のような点です。
- どの権限を要求しているのか
- その権限がアプリ内でどう使われるのか
- ユーザーが権限の内容を確認できるのか
- 申請内容とアプリの挙動が一致しているのか
そのため、説明動画を作り直しました。
権限の詳細画面をタップするところも含めて、Google のレビュアーが確認したいであろう操作を一通り入れました。
審査担当者に説明するための動画なので、権限の確認画面で見せられるものを見せて、Google OAuth でなぜその権限が必要なのかを示す必要があります。
ここを勘違いすると、差し戻されやすいのだと思います。
プライバシーポリシーも更新した
さらに、Google から次のような指摘も来ました。
Google データの取り扱いと安全対策が不明確
これを受けて、プライバシーポリシーを書き直しました。
単に「個人情報を適切に扱います」では弱いです。
Google から取得するデータについて、より具体的に書く必要がありました。
例えば、以下のような観点です。
- Google データをどのように取り扱うのか
- 第三者に提供するのか
- 安全対策はどうしているのか
プライバシーポリシーを修正し、Google に返信しました。
Google からの連絡は、だいたい3〜4日ごとに返ってくる感じでした。
すぐに終わるものではなく、指摘を受けて修正し、また数日待つ、という流れです。
OAuth 審査では、Google が何を確認したいのかを理解したうえで、動画やプライバシーポリシーを用意する必要がありました。
しっかり用意したつもりでも、審査側の観点とずれていると差し戻されます。
クローズドテストの参加者集めが本当に大変だった
次に大変だったのが、Google Play のクローズドテストです。
個人開発者にとって、ここが一番しんどいかもしれません。
アプリを作ることよりも、テスターを集めることの方が大変でした。
ネットで募集しても、ほとんど集まらない
X や Zenn 経由でも募集しました。
ただ、正直なところ、そこから来てくれた人は1〜2人いたかも、という程度でした。
自分の場合は、ネット上で一般的に「クローズドテストに協力してください」と発信しても、ほとんど集まりませんでした。
「これは普通に募集して待っているだけでは無理だな」と思いました。
最初は身内にお願いした
まずは身内にお願いしました。
妻と子供3人に入れてもらい、4人を確保しました。
ただ、これだけでは足りません。
さらに参加者を集める必要があります。
LINE グループも使った
昔からの友人グループの LINE にも連絡しました。
「アプリを作っていて、クローズドテストに協力してほしい」とお願いして、1名確保できました。
さらに、ワンコ仲間の LINE グループにも連絡しました。
ここでは、趣味で個人開発していることをばらす形になりました。
ただ、みんな iPhone でした。
「協力したいけど、iPhone なので……」という感じです。
それでも、1名協力してくれる人を確保できました。
クローズドテストの募集では、そもそも Android ユーザーを見つけるところから大変です。
会社の Slack でもお願いした
会社の Slack でも、前回の Zenn 記事を共有しました。
AI を使ってアプリを作って、Google Play のクローズドテストまで来た、という話をした上で、テスト協力をお願いしました。
仕事とは直接関係ない個人開発の話ですが、AI 活用やアプリ開発の実例として紹介しやすかったのはよかったです。
ただ、ここでもやっぱり iPhone が多い印象でした。
X の DM で直接お願いもした
さらに、しばらく会っていない Android 系のエンジニアの方にも、X の DM で直接お願いしました。
これは、けっこう勇気がいります。
でも、一般投稿で待っているだけでは集まらないので、個別にお願いする方が現実的でした。
クローズドテストの参加者集めは、技術ではなく、人間関係とお願い力の世界でした。
12人集めるには交友関係がメインだった
結果として思ったのは、12人集めるには交友関係がメインになる、ということです。
ネット経由で一般的に募集しても、基本的にはかなり厳しいと思いました。
特に個人開発の初期アプリは、まだ知名度も信用もありません。
「面白そうだから入れてみる」という人は、なかなかいません。
クローズドテストの参加者集めには、技術力とは別の能力が必要でした。
- 普段の交友関係
- 直接お願いする
- Android ユーザーを見つける
このあたりが必要になります。
コードを書くより、こっちの方が大変だったかもしれません。
妻のフィードバックが一番厳しかった
クローズドテストに入ってもらったあと、妻に感想を聞きました。
返ってきたのは、かなり厳しい意見でした。
編集できない
⇒ 使えない
別のアプリに共有するボタンがない
⇒ 使えない
「使えない」が2回来ました。
自分としては、写真が見られて、自分のクラウドにバックアップできれば満足でした。
でも、実際に使う側から見ると、それだけでは足りませんでした。
ギャラリーアプリとして使うなら、写真を見たあとにやりたいことがあります。
- 切り取りたい
- 文字を入れたい
- 矢印や丸を付けたい
- 共有したい
つまり、写真を「見る」だけではなく、「ちょっと加工して送る」ことが日常的には必要でした。
これは作っている側の自分だけでは気づきにくい部分でした。
開発者目線では、クラウド連携やバックアップの方に意識が向いていました。
でも、ユーザー目線では、もっと手前の「写真をちょっと編集して共有したい」というニーズの方が重要でした。
身近な人のフィードバックは厳しいですが、かなり本質的でした。
2週間で簡易編集機能を追加した
妻のフィードバックを受けて、簡易編集機能を追加することにしました。
追加したのは、例えば以下のような機能です。
- 文字入れ
- 円の配置
- 矢印の配置
- モザイク加工
- 簡単な切り取り
- 別アプリへ共有につながる操作
だいたい2週間ぐらいで実装しました。
この追加だけで、コード量はかなり増えました。
正式リリース 1.0.0 のソースリスト
$ find . -name "*.dart" | xargs wc -l
322 ./.dart_tool/flutter_build/dart_plugin_registrant.dart
24 ./lib/app.dart
682 ./lib/features/cloud/gallery/logic.dart
69 ./lib/features/cloud/gallery/state.dart
378 ./lib/features/cloud/gallery/view.dart
933 ./lib/features/cloud/viewer/logic.dart
65 ./lib/features/cloud/viewer/state.dart
438 ./lib/features/cloud/viewer/view.dart
間も見る
534 ./lib/features/common/gallery/gallery_scroll_anchor.dart
218 ./lib/features/common/gallery/grid_transition_controller.dart
141 ./lib/features/common/gallery/logic_helpers.dart
593 ./lib/features/common/gallery/page_logic_base.dart
141 ./lib/features/common/gallery/page_state_base.dart
29 ./lib/features/common/gallery/view_helpers.dart
32 ./lib/features/common/logic/cloud_folder_selection_tiles.dart
81 ./lib/features/common/logic/local_folder_selection_tiles.dart
53 ./lib/features/common/logic/selection_action_flow.dart
17 ./lib/features/common/menu_helpers.dart
151 ./lib/features/common/viewer/bottom_action_bar.dart
198 ./lib/features/common/viewer/logic_helpers.dart
576 ./lib/features/common/viewer/page_logic_base.dart
248 ./lib/features/common/viewer/page_state_base.dart
28 ./lib/features/common/viewer/state_constants.dart
1199 ./lib/features/home/controller/cloud_controller.dart
334 ./lib/features/home/controller/local_cloud_controller.dart
224 ./lib/features/home/controller/local_cloud_dialogs.dart
620 ./lib/features/home/controller/local_cloud_upload_controller.dart
828 ./lib/features/home/controller/local_controller.dart
419 ./lib/features/home/controller/settings_controller.dart
163 ./lib/features/home/folder_sort.dart
15 ./lib/features/home/home_layout.dart
238 ./lib/features/home/logic.dart
153 ./lib/features/home/state.dart
578 ./lib/features/home/view.dart
128 ./lib/features/home/widgets/cloud_preview_grid.dart
117 ./lib/features/home/widgets/local_preview_grid.dart
118 ./lib/features/licenses/view.dart
618 ./lib/features/local/gallery/logic.dart
75 ./lib/features/local/gallery/state.dart
385 ./lib/features/local/gallery/view.dart
804 ./lib/features/local/viewer/logic.dart
70 ./lib/features/local/viewer/state.dart
383 ./lib/features/local/viewer/view.dart
30 ./lib/features/media_editor/history/editor_history.dart
217 ./lib/features/media_editor/layers/brush_layer.dart
35 ./lib/features/media_editor/layers/editor_layer.dart
400 ./lib/features/media_editor/layers/shape_layer.dart
422 ./lib/features/media_editor/layers/text_layer.dart
142 ./lib/features/media_editor/logic/blur_logic.dart
146 ./lib/features/media_editor/logic/brush_logic.dart
45 ./lib/features/media_editor/logic/crop_logic.dart
345 ./lib/features/media_editor/logic/gesture_logic.dart
166 ./lib/features/media_editor/logic/history_logic.dart
109 ./lib/features/media_editor/logic/object_logic.dart
441 ./lib/features/media_editor/logic/render_logic.dart
163 ./lib/features/media_editor/logic/selection_logic.dart
94 ./lib/features/media_editor/logic/shape_logic.dart
199 ./lib/features/media_editor/models.dart
163 ./lib/features/media_editor/modes/blur_mode.dart
175 ./lib/features/media_editor/modes/brush_mode.dart
58 ./lib/features/media_editor/modes/crop_confirm.dart
75 ./lib/features/media_editor/modes/crop_mode.dart
609 ./lib/features/media_editor/modes/filter_mode.dart
646 ./lib/features/media_editor/modes/shape_mode.dart
534 ./lib/features/media_editor/modes/text_mode.dart
1500 ./lib/features/media_editor/modes/video_mode.dart
524 ./lib/features/media_editor/painter/image_painter.dart
439 ./lib/features/media_editor/painter/video_painter.dart
86 ./lib/features/media_editor/parts/brush_history.dart
96 ./lib/features/media_editor/parts/color_palette.dart
238 ./lib/features/media_editor/parts/crop_geometry.dart
50 ./lib/features/media_editor/parts/object_geometry.dart
58 ./lib/features/media_editor/parts/viewport_gesture.dart
273 ./lib/features/media_editor/state.dart
118 ./lib/features/media_editor/transforms/crop_transform.dart
84 ./lib/features/media_editor/transforms/viewport_transform.dart
771 ./lib/features/media_editor/view.dart
303 ./lib/features/settings/logic.dart
47 ./lib/features/settings/state.dart
231 ./lib/features/settings/view.dart
523 ./lib/features/trash/gallery/logic.dart
63 ./lib/features/trash/gallery/state.dart
400 ./lib/features/trash/gallery/view.dart
335 ./lib/features/trash/viewer/logic.dart
44 ./lib/features/trash/viewer/state.dart
303 ./lib/features/trash/viewer/view.dart
10 ./lib/main.dart
757 ./lib/services/cloud/cloud_gallery_service.dart
13 ./lib/services/cloud/cloud_gallery_service_subclass.dart
511 ./lib/services/cloud/cloud_media_cache_service.dart
348 ./lib/services/cloud/cloud_media_registry.dart
301 ./lib/services/cloud/cloud_media_registry_metadata.dart
34 ./lib/services/cloud/cloud_object_naming.dart
114 ./lib/services/cloud/cloud_object_storage.dart
23 ./lib/services/cloud/cloud_storage_service.dart
642 ./lib/services/cloud/cloud_thumbnail_cache_service.dart
586 ./lib/services/cloud/gcs_cloud_object_storage.dart
327 ./lib/services/cloud/google_cloud_auth_service.dart
801 ./lib/services/cloud/s3_compatible_cloud_object_storage.dart
185 ./lib/services/common/cancelable_request_queue.dart
133 ./lib/services/common/decoded_image.dart
685 ./lib/services/common/image_utils.dart
50 ./lib/services/common/network_connection_service.dart
216 ./lib/services/common/video_thumbnail_generator.dart
346 ./lib/services/config/app_settings.dart
57 ./lib/services/config/app_settings_repository.dart
336 ./lib/services/config/app_settings_store.dart
43 ./lib/services/config/cloud_provider.dart
139 ./lib/services/config/cloud_secret_store.dart
68 ./lib/services/config/cloud_settings.dart
53 ./lib/services/config/cloud_settings_gcs.dart
113 ./lib/services/config/cloud_settings_s3_compatible.dart
62 ./lib/services/config/local_folder_cloud_destination.dart
28 ./lib/services/firebase/firebase_app_service.dart
202 ./lib/services/gallery/gallery_cache_store.dart
81 ./lib/services/gallery/gallery_entry.dart
96 ./lib/services/gallery/gallery_label.dart
133 ./lib/services/gallery/gallery_models.dart
15 ./lib/services/gallery/gallery_mutation_result.dart
105 ./lib/services/gallery/gallery_sort.dart
85 ./lib/services/gallery/video_edit_models.dart
35 ./lib/services/local/local_folder_cloud_sync_marker.dart
1019 ./lib/services/local/local_gallery_service.dart
15 ./lib/services/local/local_gallery_service_subclass.dart
11 ./lib/services/local/local_image_path_resolver.dart
162 ./lib/services/local/local_media_cache_service.dart
385 ./lib/services/local/local_media_registry.dart
172 ./lib/services/local/local_media_registry_metadata.dart
526 ./lib/services/local/local_thumbnail_cache_service.dart
154 ./lib/services/local/storage_permission_service.dart
92 ./lib/widgets/cloud/google_auth_prompt_banner.dart
130 ./lib/widgets/common/folder_tile.dart
24 ./lib/widgets/common/slash_icon.dart
113 ./lib/widgets/common/video_file_preview.dart
98 ./lib/widgets/dialogs/cloud_settings_dialog.dart
127 ./lib/widgets/dialogs/cloud_settings_dialog_gcs.dart
159 ./lib/widgets/dialogs/cloud_settings_dialog_s3_compatible.dart
269 ./lib/widgets/dialogs/folder_selection_dialog.dart
56 ./lib/widgets/dialogs/gallery_group_dialog.dart
50 ./lib/widgets/dialogs/gallery_view_settings_dialog.dart
316 ./lib/widgets/dialogs/home_folder_action_dialog.dart
134 ./lib/widgets/dialogs/home_folder_sort_dialog.dart
143 ./lib/widgets/dialogs/local_media_permission_explanation_dialog.dart
256 ./lib/widgets/dialogs/properties_sheet.dart
161 ./lib/widgets/feedback/progress_dialog.dart
27 ./lib/widgets/feedback/snackbar.dart
181 ./lib/widgets/gallery/async_gallery_preview.dart
51 ./lib/widgets/gallery/async_gallery_preview_hero.dart
171 ./lib/widgets/gallery/gallery_grid_tile.dart
39 ./lib/widgets/gallery/gallery_interaction_layer.dart
29 ./lib/widgets/gallery/gallery_preview_error_tile.dart
17 ./lib/widgets/gallery/gallery_preview_loading_tile.dart
166 ./lib/widgets/gallery/grouped_gallery_layout.dart
220 ./lib/widgets/gallery/grouped_gallery_sliver.dart
211 ./lib/widgets/gallery/grouped_gallery_structure.dart
56 ./lib/widgets/hero/hero_flight_completion_notifier.dart
63 ./lib/widgets/hero/viewer_grid_hero.dart
67 ./lib/widgets/hero/viewer_page_hero.dart
138 ./lib/widgets/hero/viewer_route_helpers.dart
425 ./lib/widgets/scroll/scrollbar.dart
101 ./lib/widgets/scroll/scroll_aware_floating_button.dart
46 ./lib/widgets/scroll/scroll_to_top_button.dart
104 ./lib/widgets/ui_helpers.dart
41 ./lib/widgets/viewer/transparent_viewer_app_bar.dart
492 ./lib/widgets/viewer/video_fullscreen_player.dart
44 ./lib/widgets/viewer/viewer_dismiss_floating_media.dart
28 ./lib/widgets/viewer/viewer_dismiss_shell.dart
43 ./lib/widgets/viewer/viewer_helpers.dart
49 ./lib/widgets/viewer/viewer_interaction_layer.dart
23 ./lib/widgets/viewer/viewer_loading_placeholder.dart
108 ./test/app_settings_test.dart
78 ./test/cloud_object_naming_test.dart
51 ./test/cloud_thumbnail_cache_service_test.dart
103 ./test/gallery_grid_tile_test.dart
88 ./test/gallery_grouping_test.dart
56 ./test/gallery_label_test.dart
158 ./test/gallery_sort_test.dart
201 ./test/grouped_gallery_fast_scroll_label_test.dart
214 ./test/home_folder_sort_test.dart
150 ./test/image_utils_test.dart
70 ./test/media_editor_tool_selection_test.dart
248 ./test/s3_compatible_cloud_object_storage_test.dart
70 ./test/s3_compatible_settings_dialog_test.dart
12 ./test/widget_test.dart
41864 total
※前回の記事のソースリストから media_editor ってフォルダが増えてます。
簡易編集機能だけでも、1万行ぐらい増えた感覚です。
ギャラリーアプリは、画像を表示するだけなら比較的シンプルに見えます。
でも、編集機能を入れ始めると一気にアプリらしくなります。
それと同時に、作っているうちに「編集機能として、もう少しちゃんと作りたい」とも思うようになってきます。
実現できた事としては、以下です。
- 画像の上にオブジェクトを配置する
- 文字や図形を描画する
- 部分的な「ぼかし」、モザイクをかける
- 全体な「ぼかし」、フィルターを書ける
- 編集結果を保存する
- 元画像を壊さない
- 操作しやすい UI にする
- 共有しやすくする
簡易編集のつもりでしたが、実装としては結構しんどい内容でした。
そして、「クラウド対応ギャラリー」から、「日常的に使えるギャラリー」に少し近づいた感覚があります。
本番環境の利用申請は1日ぐらいで通った
クローズドテストのノルマを達成したあと、本番環境の利用申請をしました。
ここは、思ったより早かったです。
だいたい1日ぐらいで OK が出ました。
OAuth 審査やクローズドテストの参加者集めに比べると、かなりスムーズでした。
もちろん、事前に必要な情報やポリシー周りを整えていたことも関係していると思います。
ここまで来ると、ようやく「公開が見えてきた」という感じがしました。
製品申請は約2日で通った
その後、製品版として Google Play に申請しました。
こちらは約2日ぐらいで OK になりました。
Google OAuth の審査やクローズドテストで苦労した後だったので、製品申請はかなりあっさり通った印象です。
ここまで来て、ようやく Google Play で公開できました。
ファーストコミットが 2月28日。
製品公開が 5月26日。
87日で、Google Play 公開まで到達しました。
3か月未満です。
Codex を使っていなければ、この速度では無理だったと思います。
ただし、逆に言うと、Codex を使ってもなお、公開までにはこれだけの周辺作業が必要でした。
Codex で実装速度はかなり上がった
今回、Codex を使って強く感じたのは、個人開発の実装速度はかなり上がるということです。
もちろん、全部を任せれば完成するわけではありません。
仕様を考えるのは自分です。
何を作るか。
何を削るか。
どの順番でやるか。
どこまで作り込むか。
審査にどう対応するか。
ユーザーのフィードバックをどう受け止めるか。
そこは人間側の仕事です。
ただ、実装速度はかなり変わります。
特に、今回のように Flutter でアプリを作りながら、OAuth、クラウド連携、画像編集、Google Play 審査対応まで進める場合、ひとりで全部書いていたらかなり時間がかかったと思います。
Codex があることで、以下のような作業をかなり高速に進められました。
- 実装のたたき台を作る
- 既存コードに合わせて修正する
- UI を調整する
- エラーの原因を探す
- テストしながら直す
- 面倒な処理を書く
- 似たような画面や処理を展開する
自分が方向性を決めて、Codex に実装させて、さらにチェックして進める感じです。
その結果、ファーストコミットから3か月未満で Google Play 公開まで到達できました。
これは、かなり大きい変化だと思います。
今回学んだこと
今回の公開までで、特に学んだことをまとめます。
1. Cloudflare のセキュリティ設定は審査の邪魔になることがある
Bot 対策や海外アクセス制限は便利です。
ただ、Google の審査 Bot がアクセスできないと、ホームページやプライバシーポリシーの確認で落ちる可能性があります。
審査中だけでも、アクセス制限を見直した方がよさそうです。
2. OAuth 審査動画はかなり具体的に撮る必要がある
アプリを軽く操作するだけでは不十分でした。
権限の詳細を確認する操作や、その権限を使う機能の説明が必要でした。
レビュアーが迷わない動画にすることが大事です。
3. プライバシーポリシーは具体的に書く必要がある
Google データを扱うなら、何を取得して、何に使って、保存するのか、共有するのか、安全対策はどうしているのかを具体的に書く必要があります。
4. クローズドテストの参加者集めはかなり大変
12人集めるのは本当に大変でした。
一般募集だけで集めるのは、かなり難しいと思います。
結局、交友関係と直接お願いが重要でした。
5. 身近な人のフィードバックは強い
妻の「使えない」は厳しかったですが、結果的にアプリを改善する大きなきっかけになりました。
自分が満足するものを、満足するレベルで作っても、それがずれていることがあります。
6. 今どきの個人開発は、公開周辺作業が重い
今回、一番感じたのはこれです。
コードを書くこと自体は、AI でかなり速くなりました。
でも、公開するには、ひと昔前には意識しなかった審査、約款、動画、テスター集め、ストア情報整備など、周辺作業が大量にあります。
まとめ
Codex を使って Flutter 製のクラウド対応ギャラリーアプリを作り、ファーストコミットから3か月未満で Google Play 公開まで到達しました。
2月28日にファーストコミット。
そこから約2カ月で Google Play のクローズドテスト開始。
そして、5月26日に製品公開。
日数でいうと87日です。
クローズドテスト開始後は、テスター集め、Google OAuth 審査、プライバシーポリシー修正、説明動画の作り直し、簡易編集機能の追加、本番環境の利用申請、製品申請までを、1カ月かからずに進めました。
AI を使えば、個人でもここまでの速度でアプリを作れるのか、という驚きはありました。
ただ、それ以上に強く感じたのは、今どきの個人開発は、コードを書く以上に公開周辺作業が大変だということです。
昔、Windows アプリでは、アプリを作って、インストーラーを作って、公式サイトに置けば配布できました。
今のスマホアプリでは、そうはいきません。
Google Play Console の設定、Google OAuth の審査、プライバシーポリシーの整備、説明動画、クローズドテスト、テスター集め、本番申請、製品申請。
コードを書いたあとにも、公開までに越えるものがたくさんあります。
Codex によって、個人開発の実装速度は確実に上がりました。
でも、アプリを公開するには、審査対応、ユーザーへの説明、テスター集め、フィードバック対応といった、人間側の泥臭い作業も必要です。
実装は AI で速くなった。
でも、公開するための準備が増えている。
今回の開発で一番学んだのは、そこでした。
個人開発で Google Play 公開を目指している人や、Codex を使ってアプリ開発してみたい人の参考になればうれしいです。

Discussion