※この記事は、Claude Codeで1人開発しているSNS運用SaaS「ThreadPost」の開発日記です。
SNS運用を自動化しませんか?
ThreadPostなら、投稿作成・画像生成・スケジュール管理まで全てAIにお任せ。
完璧な報告とクリック率0%の絶望
メールのトラッキング機能を実装した。
AIは「完璧に動きます」と報告してきた。
テスト送信のログにも「送信成功」の文字が並ぶ。
でも、実際にメールのリンクを踏むと、画面は真っ白になった。
クリック率は見事に0%のまま微動だにしない。
AIの「できました」を信じて別の作業を進めていた僕は、数時間後にこの惨状に気づいた。
コードは完璧だった。インフラの文脈だけが欠落していた。

43回のコミットと見えないバグ
今日は汎用メールキャンペーンとステップメールの実装を進めた。
合計43件のコミット。新機能1件、バグ修正3件。
数字だけ見れば順調そのものだ。

しかし、その中身は泥沼だった。
AIの生成するコードは一見すると美しい。
インフラと結合した瞬間に牙を剥く。
今日はその「見えないバグ」との戦いの記録だ。
存在しないドメインへようこそ
ステップメールにCTA画像を差し込めば、コンバージョンが上がるはずだった。
Gemini 3 Proを使って、店舗オーナー向けのバナー画像を生成した。
画像は完璧だった。
「feat: CSUステップメール用CTA画像4パターン追加」とコミットした。
次はトラッキング機能だ。
メール内のリンクがクリックされたら、ログを残して目的のページへリダイレクトする。
AIに実装を依頼すると、数秒でコードを書き上げた。
「feat: メール開封・クリックトラッキング Phase 1 実装」という見事なコミットメッセージ。
テストを実行すると、AIは「トラッキングURLの生成とリダイレクトに成功しました」と誇らしげに報告してきた。
僕はそれを信じた。
そのまま別の機能の実装に移った。
それが地獄の始まりだった。
数時間後、テスト配信したメールのリンクを自分で踏んでみた。
ブラウザに表示されたのは「404 Not Found」。
URLを見ると、app.threadpost.jpという見慣れないドメインになっていた。
そんなドメインは存在しない。
完全に穴の空いたバケツ状態だった。
しんたろー:
まじかよ。せっかく作ったステップメールが全部ゴミ箱行きになるところだった。AIの「成功しました」ほど信用できない言葉はない。
原因は環境変数だった。
AIが書いたコードは、NEXT_PUBLIC_BASE_URLという環境変数を参照していた。
しかし、僕のプロジェクトで使っているのはNEXT_PUBLIC_APP_URLだ。
変数が存在しないため、AIは勝手にフォールバック用の存在しないドメインを割り当てていた。
「fix: トラッキングURLの環境変数をNEXT_PUBLIC_APP_URLに統一」で修正した。
たった1行の修正だ。でも、これを見つけるのに数時間かかった。
Next.jsの環境変数はビルド時に静的に埋め込まれる。
ランタイムで動的に変更できないため、CI/CDのテストから漏れると致命傷になる。
業界では「環境変数の不整合はデプロイ後のサイレント・フェイラーの典型」と言われている。
トラッキングの実装自体は高度だった。
AIはオープンリダイレクト対策のドメインホワイトリストまで自発的に実装していた。
トラッキングURLのパラメータを書き換えて、ユーザーを悪意あるフィッシングサイトへ誘導する攻撃を防ぐためだ。
セキュリティの設計は完璧だった。
でも、肝心のURLが間違っていたら何の意味もない。
メール開封の検知には1x1ピクセルの透明画像を埋め込む枯れた技術を使った。
AIはNext.jsの非同期処理を使って、レスポンスをブロックせずにログを記録するコードを書いた。
ユーザーの体感速度を落とさないためのベストプラクティスだ。
コードの文脈は完璧だった。ただ、インフラの文脈だけが欠落していた。
「fix: トラッキングURLのフォールバックをthreadpost.jpに修正」とコミット。
これでようやく、クリック率が0%から動き始めた。
しんたろー:
1日かけてクリック率が見えるようになっただけ。企業ならテストエンジニアが弾くバグも、1人開発だと全部本番で被弾する。43件のうち何件が「AIの嘘の後始末」だったか、数えたくもない。
プレビュー画面の怪奇現象
トラッキングの泥沼から抜け出したと思ったら、今度は別の場所が壊れた。
キャンペーンのプレビュー画面だ。
画面をリロードするたびに、表示されるメールの内容が最新版と古い版でランダムに入れ替わる。
完全に怪奇現象だった。
原因はSupabaseのデータ取得ロジックにあった。
僕はAIに「最新のキャンペーン設定を取得して」と指示していた。
AIは「1件だけ取得するから効率的です」という説明付きでコードを出力した。
確かに開発初期はそれで動いていた。
でも、MEOキャンペーンを追加したことで、DBの中に似たような条件のデータが複数存在するようになった。
AIが書いた曖昧な検索条件は、複数のレコードをヒットさせていた。
しんたろー:
複数マッチしてるのにエラー吐かずに1件だけ返すの、本当にやめてほしい。エラーで落ちてくれた方が100倍マシ。リロードするたびに別のデータが出てくる画面、ホラーすぎる。
データベースの便利メソッドは、複数マッチした場合に「どれか1件」を返す仕様になっていることが多い。
暗黙のソート順に依存しているため、結果が予測できない。
データが増えると牙を剥く典型的なORMの罠だ。
解決策はシンプルだった。
曖昧な検索を捨てて、IDベースの完全一致検索に切り替えた。
「fix: プレビューAPIをID検索に統一(like検索を廃止)」とコミットした。
これでようやく、怪奇現象は収まった。
ついでにメール装飾のバグも直した。
「fix: メール装飾の見出しスキップ対応とバックアップ」だ。
見出しブロック内のテキストに黄色マーカーを適用すると、文字が消える現象が起きていた。
CSSのインラインスタイル適用時、継承の優先順位を考慮しないと背景色と文字色が同化する。
メールクライアントは独自のCSSレンダリングエンジンを持つため、インライン化は必須の儀式だ。
AIは「スタイルを当てました」と言うが、Gmailで開くと真っ白になる。
これもまた、AIの「できました」の罠だった。
さらに、名前なしリードの挨拶も修正した。
「fix: 名前なしリードの挨拶を「皆さん」から自然な表現に変更」とコミット。
1対1のメールで「皆さん」は一斉送信感が出すぎる。
こういう人間臭い微調整は、まだAIには任せられない。
ここまで読んだあなたに
今なら無料で全機能をお試しいただけます。設定後は完全放置でプロ品質の投稿を毎日生成。
落とし穴:AIは存在しないインフラをデバッグできない
AIに「トラッキング機能を作って」と依頼した。
数秒で完璧なコードが生成された。
そのコードが参照する環境変数が「存在しないURL」だった。
AIはローカルのコードベースを読み込むことはできる。
でも、本番環境のクラウド設定や、そこに登録されているドメインまでは見通せない。
コードの文脈は完璧でも、インフラの文脈が欠落している。
「動くコード」を書くのは速い。
「存在しないインフラ」をデバッグする能力はゼロだ。
画面上でいくら「成功しました」と言われても、現実のブラウザでは404エラーが出る。
次からは、AIにコードを書かせる前に環境変数の一覧を渡す。たぶん。
今日の数字
| 指標 | 今回の数字 | 比較対象 |
|---|---|---|
| コミット数 | 43件 | 先週の平均は1日15件 |
| 新規機能数 | 1件 | バグ修正に追われて予定の1/3 |
| キャンペーン実装 | 1日 | 一般的なSaaS開発なら仕様策定だけで1週間 |
| メール送信コスト | ほぼ$0 | マネージドSaaSなら月額数万円+人件費 |
今日は43件のコミットを積んだ。
先週の平均が1日15件だったので、約3倍のペースだ。
ただし、その大半がAIの書いたコードと現実のインフラのズレを修正する作業だった。
新機能は1件。残り42件のうち3件がバグ修正で、残りはリファクタと調整だ。
AWS SESとSupabase Edge Functionを組み合わせることで、メール送信コストをほぼゼロ化した。
AWS SESの送信コストは100万通で約1ドルと言われている。
マネージドなメール配信SaaSの月額数万円と比べると、1/100以下のコストだ。
ただし、その代償として「全部自分で直す」というプレッシャーがのしかかる。
浮いたお金の分だけ、僕の寿命が削られている気がする。
配信停止のカウントダウン
トラッキングは動くようになった。
でも、配信停止の処理がまだ怪しい。
自社ドメインに切り替えたのはいいが、SESのバウンスレートの数字が想定より高い。
スパム判定のアルゴリズムに引っかかっている気がする。
この数字が閾値を超えたら、アカウントごと止められるかもしれない。
FAQ
AIが「成功した」と言ったのに動かなかった。どうやって原因を特定した?
AIの報告を無視して、自分でエンドツーエンドのテストをした。
具体的には、実際にメールを送って自分でリンクを踏んだ。
ブラウザのURLバーに「存在しないドメイン」が表示されて、初めて環境変数の不整合に気づいた。
メール送信のインフラコストはどう抑えている?
AWS SESを使っている。100万通で約1ドルという単価で、マネージドSaaSの1/100以下だ。
ただし、バウンス対策・トラッキング・配信停止処理は全て自前で実装する必要がある。
浮いたコストの分だけ、開発者の工数が投入されている。
配信停止URLを自社ドメインにした技術的な理由は?
共有ドメインのリンクを使うと、他の利用者の悪影響を受けてスパムフィルターに弾かれやすくなる。
自社ドメインでホストし、SPFやDKIMの整合性を保つのがメールインフラ構築の基本だ。
今回はそこまで整えたのに、トラッキングURLが存在しないドメインを指していた。本末転倒だった。
結局、最後に泥を被るのは自分
AIのコードは速いが、現実のインフラと結合した瞬間のバグは全部自分で被るしかない。

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