AIユーザー数記事にCodex折れ線グラフを追記し、デプロイスクリプトにpre-deploy検証を組み込んだ話

開発mdx-playground

AIユーザー数記事にCodex折れ線グラフを追記し、デプロイスクリプトにpre-deploy検証を組み込んだ話

9億の隣に700万の棒を置くと、700万の棒は画面から消える。今日はその「消える」を確認したうえでチャート化をやめる判断をし、代わりに描けるものだけを描いた。後半はデプロイが2回転び、1回目の転び方をスクリプトの恒久改善に変えた。

「1週間前にまとめた記事」を探すところから

ChatGPTのユーザー数、Codexのユーザー数、Claudeのユーザー数、Claude Codeのユーザー数。それぞれデータが出ていたはずだと思い、Claude Codeに調べさせて記事にまとめることにした。1週間ぐらい前に似たことをまとめた記憶があったので、新規記事ではなくそこへの追記を指定した。

探させたら、記憶より新しかった。前日(7月15日)に書いた「世界でAIを使っているのは800万人」は本当かがそれで、ChatGPT週間9億人の確認とフェルミ推定をやった記事だ。ここに4サービスのユーザー数比較を追記する方針で調査を進めさせた。

追記までの調査は次の順で回してもらった。

  • 主要4サービス(ChatGPT / Codex / Claude / Claude Code)のユーザー数データを収集
  • 一次ソースに近い記事まで遡って数字を検証
  • 発表日付の確定と、サードパーティ推計の裏取り
  • そのうえで前日の記事の終盤に追記セクションを追加

追記後、SSR出力に追記セクションの全要素が反映されていることまで確認させて、いったん調査パートは完了とした。

発見: コーディングエージェントの利用者は2桁小さい

数字を並べて気づいたのが規模の断層だった。ChatGPTやClaudeのようなチャット型AIの利用者は億の単位で数えるのに対して、CodexやClaude Codeのようなコーディングエージェントの利用者は数百万の単位になる。およそ100分の1、桁で言えば2桁小さい。

区分利用者の桁
チャット型AIChatGPT、Claude億(ChatGPTは週間9億人)
コーディングエージェントCodex、Claude Code数百万

「まだ世界中で800万人しかAIを使っていない」というネタを前日の記事で「8億の読み違い」と検証したばかりだったが、皮肉なことに、コーディングエージェントに限定すれば「数百万人しか使っていない」はほぼ実態どおりになる。

チャートにするか、しないか

最初は単位を揃えることを考えた。週間アクティブと月間アクティブが混在しているので月換算に寄せるか、と。しかし換算したところで規模の断層は変わらないし、換算係数の仮定が増えるだけで比較の精度は上がらない。月換算はやめた。

次に4サービスを1枚のチャートに並べることを考えて、これもやめた。9億・10億のスケールに700万を置いたら、コーディングエージェント側の系列は軸に張り付いて読み取れなくなる。対数軸にすれば描けるが、「100分の1」という発見自体が対数軸では直感から逃げる。チャートにすると規模差がかえって分からなくなるケースだと判断して、比較チャートは作らないことに決めた。

Codex単体なら折れ線にできる

ただ、Codex単体なら話が別だった。時系列の発表値が複数あるので、成長の折れ線グラフとして成立する。Codexだけ折れ線にしてくれ、と指示した。

作図はsvg-diagramスキルの手順に沿って進めさせた。

  • 既存記事の図とスタイルを揃えるため、スキルのリファレンスと既存SVGを先に確認させた
  • 描いたSVGはlintスクリプトで機械検証を通した
  • dev環境で記事を開き、目次からCodex節にジャンプして図を表示。図の下部しか見えていなかったのでスクロールし、スパイク部分は拡大して、ラベルの重なり・見切れがないことまで確認させた

これで完了報告が来たのだが、「表示確認した?」と聞き返した。確認していたのはチャート部分だけで、追記セクション全体は通しで見ていなかったからだ。あらためて追記セクションを上から下までスクロールさせ、localhost:3000の実描画スクリーンショットで全要素を確認してもらった。図単体のQAと、セクション全体のQAは別物だとあらためて思う。

デプロイ1回目: prerender完了後の失敗

記事が仕上がったのでデプロイに進んだ。手元で powershell -ExecutionPolicy Bypass -File scripts/measure-deploy.ps1 を実行した。これは各フェーズの所要時間を計測しながらビルドとCloudflare Pagesへのデプロイを進めるスクリプトで、prerenderが全ページ通ったあとの最終段階で赤く落ちた。

生ログは巨大になりうるので、全文は読ませず末尾とエラー行だけ抽出して原因を切り分けさせた。結果はこうだった。

  • ビルド自体は成功していた
  • 落としたのは最後の漏えい検査 verify-unpublished-excluded。「公開中の日記ページから非公開記事へのリンク6件」を検出して意図的にexit 1していた
  • つまり検査機構は正常動作。ログの文字化けはPowerShellのコードページ由来で、失敗の本体とは無関係だった

原因は unpublished: true の記事6本に対して、公開記事5ファイルがマークダウンリンクを張ったままだったこと。ソース検査では計11箇所。リンクを修正させ、検査スクリプトの再実行で全件解消を確認し、devサーバーで実描画も見て、issueを解決済みに更新した。

pre-deploy validationの組み込み

ここで引っかかったのは、修正そのものより検出のタイミングだ。非公開リンクの漏れはソースを見れば分かるのに、15分のビルドを待った後に教えられる。ビルド前に落とせるはずだと思い、pre-deploy validationの組み込みを指示した。これは結構重要な改善だと思う。

  • measure-deploy.ps1 の先頭に、ビルド前の非公開リンク検査を組み込ませた
  • 検証は3経路。ps1の構文チェック、わざと漏れファイルを一時作成して検出が働くこと(fail経路)、削除して通ること(pass経路)
  • /deploy コマンド側の導線も横展開で確認させた

これで次回から、非公開リンク漏れは15分のビルド後ではなく、実行開始2〜3秒のpre-deploy validationで即座に落ちる

デプロイ2回目: 別の原因、そして成功

再度 measure-deploy.ps1 を実行したら、また落ちた。ただし今度は入れたばかりの事前検証は2秒で通過していて、原因が別だった。落ちたのは verify-blog-payload で、「/blogページに7月の記事108本中1本が載っていない」という検出だ。

切り分けは次の順で進めさせた。

  • まず新しい生ログの末尾を確認。前回とはエラーの出所が違うことを特定
  • vuePageArticles側に該当がないことを確認
  • 欠けた記事の公開日である7/14は公開記事6本と多い日だったので、カレンダー側に「1日あたりの表示上限」がないかを疑って確認
  • nodeでpayloadを直接読み、devalue形式の参照インデックスを解決して中身を突き合わせ

結果、再ビルドで直る可能性が高い一過性の欠落と判断した。恒久対策は入れず、もう一回行くことにした。

3回目はClaude Codeにバックグラウンドで走らせた。ビルドは15分前後。完走してCloudflare Pagesへの本番反映と本番検証まで確認できた。今日の失敗は2種類あって、片方(非公開リンク漏れ)は再発防止を入れ、もう片方(payloadの一過性欠落)は「特に入れずにもう一回やった」が正確なまとめになる。

学び

  • 規模が2桁違うデータは、1枚のチャートに並べた時点で小さい側が読めなくなる。「チャートにしない」も立派な可視化の判断
  • 単位換算(週間→月間)は仮定を増やすだけで、桁の断層があるときは比較の助けにならない
  • 描けるものだけ描く。4系列の比較は無理でも、Codex単体の時系列なら折れ線として成立する
  • 図単体の目視確認と、追記セクション全体の通し確認は別のQA。「表示確認した?」と聞き返してよかった
  • ビルド後にしか分からない失敗のうち、ソース検査で分かるものは事前検証に前倒しする。15分待ちが2〜3秒になる
  • 検証系スクリプトを改修したら、fail経路とpass経路の両方をわざと作って動作確認する
  • 同じ「デプロイ失敗」でも原因が違えば対処も違う。恒久対策を入れるものと、再実行で流すものを切り分ける