自分が売っている技術書の「無料で読める試し読み」を、初めて買い手の目で読み直した。
リンクが切れていないか、配信が届いているか——そういう点検は何度もやってきた。
だが「そこに書いてあることは本当か」を確かめたことは、一度もなかった。
読み直したら、書いてあることと現物が違う箇所が次々に出てきた。数えたら17か所あった。
そして食い違いの向きが、全部同じだった。
何が出たか
いちばん重かったのはこれだ。無料章の「次章以降で解決する痛み」という表に、こう書いてあった。
第5章: 「3時間のセッションが後半ボロボロになった」→ コンテキスト劣化の検知と自動リカバリ
実際の第5章は「ファイル消失から5分で復旧する仕組み」だった。まったく別の話だ。
しかも全100章を横断で「劣化」「品質が落ち」などの語を当てても、その痛みを扱う章は見つからなかった。
つまり、持っていない中身を、購入を決める場で約束していた。
他にもこういうものが出た。
| 書いてあった数 | 現物 |
|---|---|
| 同梱している example hook は667個 | 909本 |
| 本編は全30章 | 34章 |
| 全100章・約57万字 | 約61万字 |
| (別の本)本書は全7章 | 9章 |
| 「6月15日まで残り18日」 | その日から7週間が経過 |
止まったカウントダウンが特にひどい。読者はいま開いて「残り18日」を読む。
締切は2か月近く前に過ぎている。
気づいたこと:間違いは全部「少なく」出ていた
667対909。30対34。57万対61万。7対9。
誇張は1件も無かった。全部が過少申告だった。
これは偶然ではなく、構造だと思う。過大に書けば買った人が怒る。過少に書いても誰も怒らない。
だから過大な主張は自然に淘汰され、過少な主張だけが残り続ける。
本は毎月章が増えるのに、説明文は書いた日のまま止まる。誰も文句を言わないので、
自分で数えない限り、永久に残る。
どう検算したか
以下はClaude Code(うちの自律ループ)に自分でやらせた検査だ。手順は他の商品にもそのまま使える。
1. 「約束」と「現物」を機械で突き合わせる
本文の中で「第N章: 〜」と書いている箇所を全部拾って、その説明が実際の第N章の題名と
どれくらい重なるかを測る。日本語なので2文字ずつに割って集合の重なりを見た。
def bigrams(s):
s = re.sub(r'[\s、。「」『』()()・::—\-→+\d]', '', s)
return set(s[i:i+2] for i in range(len(s) - 1))
# 「第N章: 説明」を拾って、実際の題名との重なりが低い順に出す
for m in re.finditer(r'第(\d{1,3})章[^\n]{0,12}?[::][ ]*([^\n|]{8,80})', line):
n, desc = int(m.group(1)), m.group(2)
a, b = bigrams(desc), bigrams(real_title[n])
jaccard = len(a & b) / max(1, len(a | b))
重なりがゼロだった1件が、上の「第5章」だった。
2. ★検出の式は、緩めても締めても外れる
ここが今回いちばん学びになった。
章数の食い違いを探す式を緩く書くと 182件出た。中身を見ると、ほとんどが
「第3章まで無料」(無料の範囲の話)や「第10〜12章に置いています」(章の範囲の指定)で、
章の総数の話ではない。偽陽性が支配していた。
そこで締めたら 4件まで減った。だが今度は本物を落としていた。
# 緩すぎ: 「第3章まで無料」も「第56章で扱う」も拾ってしまう
COUNT = re.compile(r'(\d{1,3})\s*章')
# 締めすぎ: 「21章で、…」のような実際の総数の申告を落とす
COUNT = re.compile(r'(?:全|本編)\s*(\d{1,3})\s*章')
最終的にどうしたか。中間の広さで22件出して、目で切り分けた。
自動検出の式は、探しているものではなく「その式が拾える形」しか拾わない。
そして締めた側の外れ方は「取りこぼす」方向なので、静かで、気づけない。
「0件でした」は、無いときと、探せていないときの両方から出る。
3. 「見つからなかった」を信じる前に、対照を置く
だから、検査が0件を返したときは必ず3つ確かめた。
- 母集団は空でないか(正しい形の記述が何件あるか数える)
- 式は仕込んだ誤りを拾えるか(わざと間違った文字列を作って当てる)
- 絶対に存在しないもので落ちるか
3番目が効いた例を挙げる。CLIのオプションが実在するかを確かめる時、こう書きたくなる。
claude --some-option --version # → バージョンが出た=実在する?
これは何も測っていない。--version が未知オプションの検査より先に処理されて終了するからだ。
絶対に存在しない名前を同じ形で流すと、それも「通る」。
timeout 20 claude --zzz-definitely-not-real </dev/null
# → error: unknown option '--zzz-definitely-not-real' ← 対照が落ちる
timeout 20 claude --max-turns 1 </dev/null
# → Error: Input must be provided ... ← 別の理由で止まる=実在する
対照が落ちない試験は、何も測っていない。
4. 母集団が自分の書いたコードで切れていた
同じ夜にもう一度やられた。別の投稿サイトの記事を数えたらちょうど120本で止まった。
きりが良すぎる。別の経路で総数を取ったら141本だった。
while page <= 20: # ← 20ページ × 6件 = 120本でちょうど切れていた
きりのいい数が出たら、まず自分の数え方の上限を疑う。
いちばん効いた直し方は「数字を書き直す」ではなかった
公開済みの記事11本が、この本を「26章」と書いていた(現物34章)。
最初は26を34に書き換えるつもりだった。だがこの本は毎月章が増える。
次に章を足した日に、また11本が同時に古くなる。
だから直し方を変えた。総数の言及そのものを外す。
「26章で整理」→「体系立てて整理」。一度直せば、以後どれだけ増えても正しいままだ。
育つものの分量を、外の面に固定の数字で書かない。
書いていいのは増えない数(価格・無料で読める範囲)だけだ。
是正は「その時見ていた面」で止まる
一晩で6回、同じ形を踏んだ。
- 6月の訂正を配った時、同じ主張を持つ別の本の第1章を飛ばした
- 販売文を直した時、同じ数字を書いている無料章を飛ばした
- 無料章を直した時、販売文の残り3冊を飛ばした
- 商品を直した時、その商品を紹介している記事を数えていなかった
- READMEを直した直後に、そのREADMEが案内している配布ページ211枚が残っていた
- 記事の側で何週間も直してきた同じ案内が、自分の配っている道具のREADMEには手つかずで残っていた
訂正を配る時は、その主張を持つ面を横断で数えてから配る。
「直した」と思った時に残っているのは、たいてい自分がその時見ていなかった面だ。
最後に、自分もやった
この記事の元になった修正の中で、私は「Kindle版は当時の全44章のまま」と書いて公開した。
44は別の版の章数で、Kindle版の中身を確かめた数ではなかった。
2時間後に自分の作業記録を読み直して気づき、断定を外した。
「確かめていない数を断定するな」と直して回っている最中に、同じ型で滑った。
検算の対象に、自分の是正の文も入れておいたほうがいい。
こうした「静かに間違ったまま動き続ける」型の事故を、実際に起きたものから逆算して
症状から引ける形にまとめた本がある。第3章まで無料で読める。