無人運用の記事バリデーション — 「終了日が無い」を書き漏らしと未公表に機械で仕分ける
セール情報を毎日無人で追記していく静的サイトを運用していると、記事データの品質チェックを人手のレビュー無しで通さざるを得ない。ここで地味に効くのが「必須フィールドが空のとき、それをエラーにするか許容するか」の設計だ。とくに期間限定セールの終了日は、読者がいちばん知りたい情報でありながら、「書き忘れた」場合と「調べたが公表されていない」場合の両方がある。この2つを同じ扱いにすると、記入漏れを素通りさせるか、正当な記事まで弾くかのどちらかに倒れる。
実際に運用しているセール速報サイト manga-deals では、この判定をビルド時の機械バリデーションに落とし込んでいる。同じ設計を JavaScript で組み立ててみる。
問題: 空欄の意味は一つではない
記事は次のような JSON で持っている。
1 | |
metrics.deadline が空のとき、ありうる状況は3つある。
- 書き漏らし: 本当は終了日があるのに入れ忘れた。これはエラーにしたい。
- 調べたが未公表: Amazon のタイムセールなどは終了日が公表されないことが実際にある。日付を推測で埋めるのは捏造なので、空欄のままにしたい。だがエラーで止めたくはない。
- そもそも期間の概念が無い記事: 「買い方ガイド」のような恒常記事(evergreen)には終了日が存在しない。これは正常。
この3つを空欄という一つの状態から区別できないのが問題の核心だ。「空欄=エラー」にすると 2 と 3 が誤検知になり、「空欄=許容」にすると 1 が漏れる。
解決: 意図を表す明示フィールドを足す
区別できないのは、状態が足りないからだ。そこで書いた側の意図を表す明示フィールドを2つ用意する。
articleType: 記事の種別。evergreenなら期間の概念が無い(状況3)。metrics.deadlineNote: 終了日が無い理由を書く欄。ここに文章があれば「調べた結果として無い」(状況2)とみなす。
空欄のまま deadlineNote も無いものだけを、状況1(書き漏らし)としてエラーにする。fail-closed —「判断できないものは通さない」— の考え方だ。
1 | |
ポイントは、警告(warn)とエラー(error)を分けていることだ。「調べたが未公表」は正当だが、読者には「終了日未公表」と見せる必要があるので、黙って通さず warn として記録に残す。ビルド自体は止めない。書き漏らしだけが error でビルドを止める。
動かして確かめる
4種類の記事を通してみる。
1 | |
同じ「終了日が無い」状態でも、b はエラー、c は警告、d は正常と、意図に応じて三者三様に分かれる。これがビルド時に走るので、無人で追記された記事のうち書き漏らしだけが確実に止まる。
応用: 「終了日を埋めなかった」ことを積極的に記録する
この設計の副産物として、deadlineNote は「調べた結果として終了日が無かった」という確認の痕跡になる。あとから記事を見返したとき、空欄が「未処理」なのか「確認済みで無し」なのかが一目で分かる。無人運用では、この「確認したという事実」を残せるかどうかが、データの信頼性を左右する。
同じ発想は終了日以外にも使える。たとえば「価格を確認したが変動するため断定しない」ことを表すフラグ、「実在を2ソースで確認した」ことを表す confirmed 日付など、空欄の意味を一意にするための明示フィールドを足していくと、レビュー無しでも品質を保ちやすくなる。
実際の運用例は manga-deals(Kindleマンガのセール速報) で見られる。各記事に確認日と出典、そして終了日(または未公表である旨)が付いているのは、ここで作ったのと同じ仕分けをビルド時に通しているからだ。
まとめ
- 「必須フィールドが空」を一律にエラーにも許容にもしない。空欄の意味が複数あるなら、意図を表す明示フィールドで一意にする。
- fail-closed(判断できないものは通さない)を基本に、「書き漏らし=error」「確認したが無し=warn」「概念が無い=ok」を分ける。
- 「確認したが無い」を記録に残せると、無人運用でもデータの信頼性を保てる。