🙄

CodexでFlutterアプリを3か月未満で公開したけど、今どきの個人開発はコード以外が大変すぎる

に公開

OpenAIのCodexを試すつもりでFlutterアプリを作り始めたら、気づいたら87日でGoogle Play公開まで到達していました。

マイギャラリー

しかし一番衝撃だったのは、コードを書く時間よりも、公開するための周辺作業の方が圧倒的に大変だったということです。

前回の記事では、Codex を試すつもりで Flutter アプリを作り始めたら、約2カ月で Google Play のクローズドテストまで到達していた、という話を書きました。

https://zenn.dev/mfactory_uh/articles/9093e103462a9d

その後、Google OAuth の審査、クローズドテストの参加者集め、家族からのフィードバック対応、本番環境の利用申請、製品申請を経て、最終的に Google Play で製品公開まで到達しました。

https://play.google.com/store/apps/details?id=me.mfactory.mygallery

ファーストコミットは 2026年2月28日。
製品公開日は 2026年5月26日。

日数でいうと 87日です。

流れとしては、ファーストコミットから約2カ月でクローズドテストまで到達し、そこから1カ月かからずに、テスター集め、Google OAuth 審査、簡易編集機能の追加、本番申請、製品公開まで一気に進めた形です。

つまり、ファーストコミットから3か月未満で Google Play 公開まで到達できたことになります。

Codex を使うことで、実装速度はかなり上がりました。

ただ、今回やってみて強く感じたのは、そこだけではありません。

むしろ一番感じたのは、

今どきの個人開発は、コードを書く以上に、公開するための周辺作業が多すぎる

ということでした。

以前作った Windows 向けの テキストエディタ gPad を作っていた頃は、公開までの流れはもっとシンプルでした。

  • アプリを作る
  • インストーラーを作る
  • 公式サイトを作る
  • ダウンロードリンクを置く

基本的には、それで配布できました。
https://mfactory.me

でも、今のスマホアプリ、特に 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