2026-09-05 · githubactions · zenn · 設計 · 個人開発 · デバッグ
二分法で正しく詰めても答えに届かなかった。失敗の理由が手元に返ってこない系のデバッグ
Zenn の GitHub 連携を使って、記事を git push で公開する仕組みを作りました。
12本まとめて push しました。
2本だけ公開され、残り10本は何も起きませんでした。
エラーはありません。連携は正常。push は届いている。デプロイの結果も「成功」。 それでも記事が出ない。
原因にたどり着くまで丸一日かかりました。技術的には難しくない話でした。 難しかったのは、失敗の理由がこちら側に一切返ってこなかったことです。
最初にやったこと(全部外れ)
まず「壊れているファイルがあるはずだ」と考えました。1本だけなら何かが違うのだろうと。
| 試したこと | 結果 |
|---|---|
| push が届いているか確認 | 届いている |
| BOM / CRLF / 制御文字 | 正常。公開できた記事と同じ構造 |
| frontmatter の差分 | 差なし |
| 空コミットで再デプロイ | 変化なし |
| 本文を変えて再デプロイ | 変化なし |
| slug の改名(2記事で) | 変化なし |
| 時間を4.5時間空けて1本だけ push | 変化なし |
| 一度も保存されたことのない新記事を単独 push | 変化なし |
最後の1行で、「特定のファイルが壊れている」という仮説は死にました。 新品のファイルでも保存されない。 つまり個々のファイルの問題ではない。
ここまでで半日使っています。全部推測を1つずつ潰すやり方でした。
二分法に切り替えた
推測をやめて、範囲を機械的に狭めることにしました。
リポジトリには記事12本と、本(10章+設定)が入っています。まず本を丸ごと削除。
- 本を削除 → 変化なし
次に記事も削り、公開済み2本と候補1本の、計3ファイルだけにしました。
- ファイル3つだけ → 変化なし
ここで結論が出ます。
リポジトリの中身は原因ではない。
これは正しい結論でした。二分法は機能しています。仮説を潰す速度も、 最初の総当たりより桁違いに速い。
それでも、答えには届きませんでした。
なぜ届かなかったか
手元でできる実験は、突き詰めると「入力を変えて、出力を見る」ことです。 二分法もその一種で、入力の範囲を半分ずつ削って、出力の変化を見る技法です。
この技法は、出力が観測できることを前提にしています。
私の場合、出力は「記事が出るか、出ないか」の1ビットしかありませんでした。 そして実際の失敗の理由は、push の応答にも、API にも、リポジトリにも返ってきません。 Zenn のデプロイ画面にだけ表示されていました。
つまり、理由が置かれている場所を、私は一度も見ていなかった。
入力をどれだけ削っても、出力が1ビットのままなら、得られる情報も1ビットです。 観測できない場所に理由がある系では、手元の実験は原理的に足りません。
実際の理由
デプロイ画面にはこう出ていました。
次の記事は投稿数の上限に達したためデプロイされませんでした: (記事名が4つ)
投稿レート制限でした。 デプロイは毎回成功していて、その中で記事だけが弾かれていた。
Zenn の FAQ を読むと、こうなっています。
- 上限のロジックは非公開(不正防止のため開示しないと明記されている)
- 記事は直近24時間以内の投稿数で判定される
- 本は直近1週間以内。記事とは独立した上限
- 上限に達しても、時間が経てば再び投稿できる
実測では、24時間で2本が通り、残りは全滅していました。
「12本を一度に push した」という、最初にやったことがそのまま原因でした。
順番が逆だった
反省点は、二分法を使ったことではありません。順番です。
やるべきだったのは、切り分けを始める前に「失敗の理由はどこに出るのか」を探すことでした。
- push の応答に出るか → 出ない
- API から取れるか → 取れない
- リポジトリに何か書かれるか → 書かれない
- デプロイ画面に出るか → 出る
この確認は5分で終わります。私は代わりに半日、返ってこない出力を見つめていました。
「エラーが出ていないから正常」ではありません。 「エラーの出る場所を見ていない」だけかもしれない。 この2つは全く違います。
同じ日に、もう1回同じ穴を踏んだ
dev.to で、公開済み記事9本を API から一括更新したときです。
3秒間隔で回したら、3本が 500 Internal Server Error で落ちました。
500 はサーバ内部のエラーです。普通に読めば「向こうが壊れている」。 10秒空けて再実行したら、3本とも通りました。
レート制限が 500 として返ってきていました。 429 ではなく。
返ってきたエラーが、起きたことを正しく表しているとは限りません。 「500 が返った」は「サーバが壊れた」ではなく、「500 が返った」という事実だけです。
自動化すると、意図せず「乱造」の形になる
もう1つ、技術とは別の話です。
12本を一度に投稿する行為は、人間には物理的にできません。 プラットフォームから見れば、それは機械が大量投稿している形そのものです。
実際、Zenn は AI によるコンテンツ執筆に関する方針を出しています。
- 機械により自動生成された文章の投稿は禁止
- AI 生成による投稿の乱造行為は、アカウント凍結の対象
- ただし AI の利用自体は禁止していない
- 求めているのは「人が主体となって情報を発信すること」=著者が内容を検証していること
私の記事は、実際に踏んだバグと実測値だけで書いています。中身は方針に反していません。 しかし出し方が、違反の形をしていました。
レート制限は、罰ではなく警告として働いたと考えるべきです。 だから、回避策を探すのは間違いです。ペースを落とすのが正しい対処です。
直したこと
公開待ちの記事を、常に1本だけにする
それだけです。公開されたら、次の1本を公開待ちにする。
自動化されたパイプラインは、放っておくと持っている分を全部出そうとします。 出せる量ではなく、出してよい量で律速をかける必要がありました。
まとめ
- 二分法は「出力が観測できる」ことを前提にした技法。 出力が1ビットしか返ってこない系では、範囲を絞れても答えには届かない
- 切り分けを始める前に、失敗の理由がどこに出るのかを探す。 これは5分で終わる
- 「エラーが出ていない」と「エラーの出る場所を見ていない」は違う
- 返ってきたエラーコードが、起きたことを表しているとは限らない (レート制限が 500 で返ってくることがある)
- 自動化した公開は、意図せず機械的な大量投稿の形になる。 出せる量ではなく、出してよい量で律速をかける
半日使った切り分けは、結論そのものは正しかったです。 ただ、ログを1行見れば5分で終わっていました。
あわせて読む
- 「動いた」を検証と見なすと、1コミットでだけ通るコードが出荷される
- 同じ記事を3か所に出すと検索評価が割れる。canonical をどこに寄せるか
- Claude Code に5人格を分担させて並列開発する
Claude Code に設計・実装・レビューを別々の人格として分担させ、GitHub Issue と
ブランチを軸に並列開発を回すための設定一式を MIT で公開しています。
コピーして ./setup.sh を叩けば動きます。技術スタックには依存しません。
https://github.com/quintetkit/quartet
このワークフローだけで実際にツールを 1 つ作りました。Issue の分割から PR、 レビュー、マージまで記録が全部残っています。うまくいかなかった箇所も消していません。
https://github.com/quintetkit/mdlinkcheck
UI 設計人格・レビュー基準・Issue 単位の並列実行スクリプト・実践ガイド 10 章を 足したものは製品ページにあります。
実践ガイド全10章は Zenn Book で読めます(¥1,500・2章まで無料)。
https://zenn.dev/quintetkit/books/claude-code-parallel-workflow
設定一式(5人格・スクリプト・ガイド全文)の配布はこちらです。