※この記事は、Claude Codeで1人開発しているSNS運用SaaS「ThreadPost」の開発日記です。
SNS運用を自動化しませんか?
ThreadPostなら、投稿作成・画像生成・スケジュール管理まで全てAIにお任せ。
完璧なデプロイの直後に起きた静寂
デプロイボタンを押した。画面には緑色のチェックマークが表示された。

次の瞬間、全ての定期実行ジョブが沈黙した。システム全体が完全に停止したのだ。
今週の全体像
今週のコミットは26件だ。新機能の追加が1件、バグ修正が4件だった。
最大の課題は投稿生成APIのタイムアウト問題だ。これを解決するためにInngestという非同期ジョブツールを導入した。
全てが順調に進んでいると思っていた。インフラの制限という見えない壁に激突するまでは。
Inngest統合でタイムアウトを粉砕する
投稿生成のAPIが限界を迎えていた。ユーザーがボタンを押すと画面が数十秒間フリーズする状態だった。
裏側では膨大な処理が直列で走っていた。AIへのプロンプト送信、レスポンスの解析、画像URLの抽出、データベースへの保存だ。
一般的なサーバーレス環境では関数の実行時間に厳しい制限がある。VercelのHobbyプランではわずか10秒でプロセスが強制キルされる。
重い処理を同期的に実行するのは、現代のSaaSアーキテクチャとして破綻している。だから僕は処理を非同期化することにした。
「feat: Inngest統合によるスケーラブルな投稿実行システムを実装」
Inngestはイベント駆動型のバックグラウンドジョブ管理ツールだ。APIはイベントを送信するだけで即座にレスポンスを返す。
実際の重い処理はInngestのワーカーが裏で引き受ける。これでフロントエンドがタイムアウト画面を表示することはなくなる。
さらにFan-outパターンを実装した。「feat: Inngest関数をFan-outパターンに変更」
1つの起点となるイベントから複数の子タスクを並列で生成する設計だ。ユーザー数が100人でも1000人でもジョブが詰まることなく並列処理される。
同時実行数を制御するconcurrency設定も入れた。外部APIのレートリミットに引っかからないようにするためだ。
これでスケーラビリティの問題は消滅した。同時に技術的負債の返済も行った。
「fix: 投稿生成ロジックをPostGenerationServiceに統合」
650行の巨大なルーティングファイルが存在していた。全ての処理がそこに詰め込まれていたのだ。僕はClaude Codeにリファクタリングを指示した。
共通ロジックをサービスクラスとして切り出した。結果、90行までスリム化した。
しんたろー:
650行のスパゲッティコードを触るたびに手汗をかいていた。今は90行。Claude Codeに投げたら一瞬で終わった。
画像処理のバグも同時に潰した。「fix: 予約投稿の画像処理完全実装」
Threadsは最大10枚、Xは最大4枚のカルーセル投稿に対応している。プラットフォームごとの制限を統一する処理で型エラーが連発していたのだ。
手動投稿と予約投稿のロジックを完全に統一した。これでどの経路から投稿しても同じ品質が担保される。
タイムアウトはゼロになった。コードの見通しも良くなった。しかし、バックグラウンド環境には特有の罠が潜んでいた。
RLSの壁と幽霊データ
バックグラウンドジョブは順調に動いているように見えた。しかしデータベースのログがおかしい。
過去に投稿した記事のURLを取得する処理が、常に空の配列を返していたのだ。「fix: Inngest環境でgetUsedArticleUrlsが0件を返す問題を修正」
これは重複投稿を防ぐための極めて重要なロジックだ。データが存在しないはずがない。
SupabaseのRow Level Securityが原因だった。Next.jsの環境ではユーザーの認証情報をクッキーで管理している。
しかし、Inngestのワーカーは非同期で動く独立したプロセスだ。そこにクッキーのコンテキストは存在しない。
認証情報がないため、データベースは権限なしと判断して空の配列を返していた。エラーすら吐かないのが一番厄介だ。
だから僕はService Role Keyを使う決断をした。「fix: Use Service Role key in Inngest function」
これはデータベースの全権限を持つマスターキーだ。本来、フロントエンドや通常のAPIで使用してはいけない。
バックグラウンドジョブの内部だけで限定的に使用する。処理が終わればコンテキストごと破棄される設計にした。
しんたろー:
エラーが出ないバグが一番怖い。データが0件で返ってくるのに気付くまで2時間無駄にした。
非同期ジョブにおけるコンテキストの喪失は、サーバーレス開発の典型的な落とし穴だ。1人開発で複雑な仕組みを作るのはオーバーエンジニアリングだ。
Service Role Keyの限定使用は、リスクとスピードの最適なトレードオフだった。これで重複記事ブロックが正常に動作するようになった。
ここまで読んだあなたに
今なら無料で全機能をお試しいただけます。設定後は完全放置でプロ品質の投稿を毎日生成。
落とし穴
デプロイした瞬間にCronが全滅し、サイトが沈黙した。Vercel Hobbyプランの「Cronジョブは2つまで」という制限を知らなかった。
6つあったCronジョブが全て停止扱いになっていたのだ。「よし、これで完璧だ!」と自信満々にデプロイボタンを押した自分を、3分後の自分が殴りたくなった。
「fix: Reduce cron jobs to 2 for Vercel Hobby plan limit」
泣く泣くCronの数を2つに減らした。1つのCronを起点にして、Inngest側でタスクを分岐させる設計に急遽変更した。
今日の数字
今回の開発で動いた具体的な数字をまとめた。

| 項目 | 今回の数字 | 比較対象 |
|------|-----------|----------|
| コミット数 | 26件 | 先週は12件 |
| APIコード量 | 90行 | リファクタリング前は650行 |
| Cronジョブ数 | 2つ | Proプランなら6つ稼働可能 |
| 並列処理数 | 1000件 | 同期処理時は10件でタイムアウト |
コード量の削減が最も大きな成果だ。650行のスパゲッティコードを90行まで圧縮した。今後の保守コストは86%削減される計算だ。
本来ならVercelのProプランに移行して月額20ドルを払う必要があった。しかし、Cronを起点にしてInngestのFan-outパターンを組み合わせた。
これにより、Hobbyプランの無料枠のままでProプラン相当の並列処理を実現したのだ。
しんたろー:
月額20ドルをケチるために設計を捻じ曲げた。でも結果的にスケーラブルになったから良しとする。
FAQ
Q: なぜ自前でRedisを立てずにInngestを選んだのか?
インフラ管理のコストを完全にゼロにするためだ。自前でワーカーサーバーとRedisを維持すると月額数十ドルかかる。
Q: Service Role Keyの漏洩リスクはどう対策しているか?
環境変数としてVercel側でのみ保持している。フロントエンドのバンドルには絶対に含めない設定だ。
Q: Cronジョブの制限に対して、他の無料SaaSに移行しなかった理由は?
VercelとNext.jsの統合エコシステムを捨てる学習コストの方が高いと判断したからだ。Cronを単なるトリガーとして割り切り、処理をFan-outさせる設計変更の方が短時間で済んだ。
まとめ
インフラの制限に激突して設計を妥協したが、結果的に強固なシステムになった。ThreadPostは今日も進化した。

この記事が参考になったら、ThreadPostを試してみませんか?
投稿作成・画像生成・スケジュール管理まで、全てAIにお任せできます。
ThreadPostをもっと知る