0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIに任せきりで作ったSaaSが、ローンチ12日間壊れていた話

0
Posted at

この記事は CodeSensei というAI学習SaaSの開発・運用を通じて学んだ、いちばん大きい反省の記録です。
未経験から独学2ヶ月でSaaSをローンチした全記録は別の母艦記事にあります。

はじめに

2026年4月15日に CodeSensei というサービスをローンチしました。AIが自分のコードを題材に、プログラミングの古典の知見を踏まえて解説するオンライン学習プラットフォームです。

着想から本番デプロイまで6日。生成のほとんどは Claude Code が担当しました。
ローンチ当日、僕は DEVLOG にこう書きました。

本番で全機能動作確認完了(AI×5機能 / チーム / Stripe決済 / 公開ページ / SEO)

12日後の今日、その記述が部分的な嘘だったことに気づきました。
プロダクトの GitHub OAuth フローは、ローンチから一度も成功していなかった

これは、その12日間に何が起きていて、なぜ気づけなかったのか、そして AI に任せきりにすることの落とし穴についての記録です。

12日後、Supabase を見て凍った

「ローンチから2週間経つけど、ユーザーって実際どれくらい使ってくれてるんだろう?」
そう思って、ふと Supabase で集計クエリを叩きました。

SELECT
  (SELECT COUNT(*) FROM auth.users) AS total_users,
  (SELECT COUNT(*) FROM auth.users WHERE created_at >= '2026-04-15') AS users_since_launch,
  (SELECT COUNT(*) FROM auth.users WHERE last_sign_in_at >= NOW() - INTERVAL '7 days') AS active_7d,
  (SELECT COUNT(*) FROM public.ai_conversations) AS total_conversations,
  (SELECT COUNT(*) FROM public.ai_messages) AS total_ai_messages,
  (SELECT COUNT(*) FROM public.lesson_completions) AS lessons_completed

結果は、こうでした。

指標 数値
総ユーザー 4
ローンチ後の新規 2
過去7日のアクティブ 1(自分のみ)
AI会話の累計 0件
AIメッセージの累計 0件
完了レッスン 0件

サインアップした人はいた。でも、誰一人、プロダクトを使った形跡がない

「ローンチが地味だったからだろう」では説明がつかない数字でした。サインアップした2人ですら、一度もAIと会話していない。これは集客の問題ではなく、サインアップした後で何かが壊れている問題です。

何が壊れていたか

CodeSensei のコア体験は「自分のコードを題材にAIが学ばせる」こと。これに到達するには2ルートあります:

  1. GitHub接続 → リポジトリ選択 → コードから学ぶ(推奨ルート)
  2. コードを手動でペースト → コードから学ぶ(フォールバック)

ローンチ前夜の「動作確認」では、(2) の手動ペーストでAIストリーミングが動くことを踏みました。だから「AI会話 ✅」と書いた。

問題は、ダッシュボードを開いたユーザーの目に最初に飛び込んでくるのは (1) のGitHub接続UIだということです。サイドバーに「GitHub連携」が見えていて、ヒーローコピーも「あなたのコードで学ぶ」と書いてある。誰だってまずGitHub接続を試します。

そして、そのGitHub OAuth フローは、12日間、一度も成功していなかった

真因 — env変数の末尾に紛れ込んだ \n

ユーザーから「github 連携できない」という報告を受けて、調査を始めました。

リダイレクト後のURLは ?error=oauth_failed。callbackに詳細ログを仕込んで再現してもらうと、エラー名が Not Found になりました。これは GitHub の OAuth エラーコードとしては存在しないもの。GitHub の API が {"message": "Not Found"} を返している、つまり送ったリクエストが「未知のエンドポイント or 未知のクライアント」と判定されている

最初は callback の body 形式(JSON vs form-encoded)を疑って修正しましたが、症状は変わらず。
そこで Vercel の環境変数を直接覗きにいきました。

$ vercel env pull .env.production.check --environment production
$ python3 -c "
import re
with open('.env.production.check') as f:
    content = f.read()
m = re.search(r'^GITHUB_CLIENT_ID=\"?([^\"\r\n]+)\"?', content, re.M)
v = m.group(1)
print(f'len={len(v)}')
print(f'repr={v!r}')
"

出力:

len=22
repr='Ov23liy6jHmiy71yxMSD\\n'

\\n です。Pythonの repr 表記なので、実際にはバックスラッシュ + n の2文字が値の末尾に入っている。

GitHub の OAuth App の Client ID は20文字。なのに env には22文字が入っていた。差分の2文字は、リテラルの \n

GITHUB_CLIENT_ID="Ov23liy6jHmiy71yxMSD\n"   ← この \n が文字列の一部
GITHUB_CLIENT_SECRET="...\n"                 ← 40文字 のはずが 42文字
NEXT_PUBLIC_GITHUB_CLIENT_ID="...\n"         ← 同上

GitHubの token endpoint に送られていたのは、client_id=Ov23liy6jHmiy71yxMSD%5Cn\n を URL エンコードしたもの)。GitHub から見れば、これは存在しないクライアントです。だから 404。だから {"message": "Not Found"}

おそらく Vercel の env 設定画面でコピペした際、改行コードがリテラルの2文字として末尾に紛れ込んだ。Vercel の入力欄は末尾の \n を弾かない。ビルドは通る、ページは表示される、API は200を返す。唯一の症状は「ユーザーが Authorize ボタンを押した後にエラーにリダイレクトされる」ことだけ。

修復は単純で、3つの env 変数をクリーン値で再登録するだけ。

vercel env rm GITHUB_CLIENT_ID production --yes
printf '%s' "$CLEANED_VALUE" | vercel env add GITHUB_CLIENT_ID production
# 同様に他の2つも
vercel --prod --yes

これで OAuth フローは即座に復旧しました。リポジトリ一覧が表示され、コア体験への動線が初めて開通しました。

なぜ12日間気づけなかったか

ここからが本当の反省です。

「実装した」と「動作確認した」を、AIは書類上区別しない

DEVLOG を読み返すと、次のような記述がありました。

GitHub OAuth連携実装

  • /api/github/callback/route.ts — OAuthコード→アクセストークン交換、github_connectionsにupsert
  • /api/github/repos/route.ts — リポジトリ一覧取得 + 接続/切断のPOST

これは AI が実装した記録です。「動作確認した」記録ではありません。
ところが、その下のセクションでは:

E2Eテスト結果 + バグ修正4件

(中略)

  • 本番で全機能動作確認完了(AI×5機能 / チーム / Stripe決済 / 公開ページ / SEO)

「全機能動作確認完了」と書いてある。でもこの一覧に「GitHub OAuth リポジトリ接続」は入っていない

AI は、コードを書き、ビルドが通ることを確認し、tsc が通ることを確認し、デプロイが Ready になったことを確認した。それを「動作確認した」と書いた。

でも、AI はブラウザで「Authorize」ボタンを押していない。GitHub からリダイレクトされてきた URL を目で見ていない。リポジトリ一覧が表示されたかを確かめていない。

AI はブラウザを操作しない。当たり前すぎて見落としていた事実でした。

「動作確認した」とAIが書いた時、それは「コードが整合している」の意味だった

AIが実行できる検証は次の通りです:

  • 型チェック (tsc --noEmit)
  • ビルド成功 (next build)
  • コードレビュー(自分で書いたコードを自分で読み直す)
  • ユニットテスト実行(書いてあれば)
  • API endpoint への HTTP リクエスト(curl 等)

AIが実行できない検証はこれです:

  • ブラウザでボタンをクリック
  • リダイレクト先のURLを目で見る
  • フォーム入力後の挙動を確認
  • 「ボタンを押した後にどんな画面が出るか」を肌で見る

OAuth フローのバグは、後者でしか検出できないクラスの問題でした。env 変数の末尾 \n は、コード上完璧でも、ビルドが通っても、API が 200 を返しても、実際にユーザーが Authorize を完走するまで露呈しない

そして AI は、それを完走しません。完走できるのは、自分の指だけです。

オーナーであるはずの僕が、AIの「動作確認完了」を鵜呑みにした

これが核心です。

ローンチ前夜、AI が DEVLOG に「全機能動作確認完了」と書いた。僕はそれを読んで、自分でも一通り踏むつもりでした。実際、サインアップ → ログイン → ダッシュボード → コース一覧 → レッスン → AI会話(手動ペースト)まで踏みました。

でも、サイドバーの「GitHub連携」タブを開いて Authorize を押す、というステップを踏まなかった
AI が「動作確認完了」と書いていたから、確認済みのつもりになっていた。

これは AI のミスではなく、僕のミスです。AI に動作確認をさせる、という構造そのものが間違っていた。

「AIに任せきり」の境界線

AI開発で「AIに任せていい領域」と「人間の指がボタンを押す必要がある領域」は、明確に違います。

任せていい 任せてはいけない
コード生成 ブラウザでボタンを押した後の挙動確認
型定義・スキーマ設計 本番URLで実際にOAuthを完走する
エラーハンドリング設計 設定後の env 値が文字列として正しいか目視
ビルド成功確認 サインアップ後のユーザー視点での体験
ドキュメント生成 「動作確認完了」と書く判断

「動作確認完了」と書く権限を、AIに与えてはいけない。これが今回の最大の学びでした。

検知が遅すぎた — メトリクス監視の仕組みが無かった

仮にローンチ翌日に Supabase を見ていれば、即気づけました。

ローンチ後12時間でサインアップ2人、AI会話 0件

これは普通じゃない。サインアップした人がアプリを開きもしないなんて、よっぽどUI が壊れているか、サインアップフローが嘘をついているかのどちらかです。

でも僕は、ローンチ後の数値を確認するルーティンを持っていなかった。次に Supabase を覗いたのが12日後。それまで、Product Hunt の upvote 数や Zenn の いいね数(いずれも極小)ばかり見ていて、「集客がうまくいってないだけ」と思い込んでいました。

集客と機能不全は別問題。バックエンド指標を見れば、集客以前にプロダクトが死んでることが即わかったはずでした。

再発防止 — 具体的にやることリスト

1. AIに「動作確認完了」を書かせない

DEVLOG のテンプレートを次のように変えました:

### 実装完了 ✅
- [機能名] のコードを書いた、ビルドは通った

### 本番動作確認(人間) ⏳
- [ ] 新規シークレットウィンドウでサインアップ
- [ ] ダッシュボード到達
- [ ] サイドバー全タブを開いて遷移確認
- [ ] [機能ごとに具体的な踏むステップ]
- [ ] ログアウト → 再ログイン

「実装完了」と「本番動作確認」を別セクションに分け、後者は人間がチェックを入れるまで未完。AI には書く権限を与えない。

2. ローンチ後の数値監視ルーティン

3日 / 7日 / 14日のチェックを Schedule に登録:

  • 3日後: サインアップ数、AI会話数、エラーログ
  • 7日後: D7 リテンション、ファネル離脱ポイント
  • 14日後: KPI 再評価マトリクス発動

「集客がうまくいかない」と「機能が壊れている」は別問題なので、両方を切り分けて見る。

3. env 変数追加直後の目視確認

vercel env add した直後に必ず:

vercel env pull .env.verify
grep "^KEY=" .env.verify | python3 -c "
import sys, re
for line in sys.stdin:
    m = re.match(r'^([^=]+)=\"?([^\"\\r\\n]+)\"?', line)
    if m:
        k, v = m.groups()
        print(f'{k}: len={len(v)} last3={v[-3:]!r}')
"
rm .env.verify

長さが想定通りか、末尾3文字に変な文字が紛れていないか。コピペ事故はこれで100%防げる。

結び

「AIで2日でSaaSを作った話」は華やかな武勇伝として書けます。実際、当時の母艦記事はそう書きました。

でも今回、12日間プロダクトを壊し続けた事実を踏まえて思うのは、武勇伝より反省の方が、たぶん人の役に立つということです。

AI で開発するのは、もう確実に未来です。生成速度・実装の質・ドキュメント生成、どれを取っても恩恵が大きい。
ただし、AIが「できた」と言った後、必ず自分の指で全部踏む。それでも漏れる。漏れた時の早期検知ルートを2つ持つ(バックエンド指標 + ユーザー報告)。

これだけは、AIに任せきりにしてはいけない領域です。

僕と同じ失敗をする人が一人でも減れば、この12日間の壊れたプロダクトにも、ちゃんと意味があったと思えます。


関連記事

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?