1 つの記事を商品ごとに分ける処理で起きた 3 つの取り違え:子が親になり、共有リンクが別の店を指し、名前が 2 つの商品のものだった

English version

トレカダッシュ 開発の記録 / 2026年9月

iOS アプリ「トレカダッシュ」には、お店の告知を、商品ごとの記事に分ける処理があります。1 つの告知に「ポケモンカードの A と B の抽選」と書いてあれば、A の記事と B の記事に分けます。分けた記事を「子」、元の記事を「親」と呼びます。

この処理のまわりで、3 つの「取り違え」が見つかりました。どれも、画面には何も出ないまま、本物の抽選が消える失敗です。

数字は、2026 年 9 月の作業の記録(9 月 5 日と 9 月 20 日の分を含む)から書いています。

1. 子に、親向けの処理をもう一度かけた(9 月 20 日)

多角的に点検していて、その日の 23:59 が締切の抽選、60 件(23 店)が、全部フィードから消えているのが見つかりました。

子の記事は、親のチラシの画像と、親の題名(splitFromTitle)を、そのまま持っています。複数の商品が書かれた記事を聞き直す処理が、この子を見て、「9 商品が載っている」と読みました。子の商品の欄は空になり、「9 件」と書き換えられました。そして翌日、総称の記事(特定の商品を指さない記事)を整理する処理が、「商品が無い総称だ」として、伏せました。

割った先の商品の ID は、すでにいる兄弟と同じなので、新しい記事は 0 件でした。その商品だけが、消えたのです。

同じ根っこの問題は、もう 1 つありました。結果発表や当選者向けの案内を見分ける判定が、子の題名は商品名だけで、本文(親の題名)が届かないので、すり抜けていました(16 件)。

直し方は、記事を集めて判定したり伏せたりする処理を足すときに、子を除く(splitFromArticleId を持つ記事を除く)か、親の題名を本文として渡すか、どちらかを必ず決めることでした。「子だから触らない」の目印には、URL の中の印ではなく、記事の項目を使います。URL の印は、応募先の張り替えで消えるからです。

2. リンクだけで親を探したら、別の店の記事が出てきた

ふるいちという店の、2 商品をまとめた告知が、10 件、分割されませんでした。親の記事を探す関数が、リンクだけで探していたからです。

この店のチェーンは、同じ共有のページを、13 店が同じリンクで持っています。リンクだけで探すと、次のどれかに当たりました。

その結果、チェーン全体の別の店の記事や、子の記事を親として、何度も分割していました。ログに「2 商品へ分割しました」が、毎回同じ親で出ていました。

リンクは、店を特定する鍵ではありません。共有のページ、引用、まとめ記事の由来で、同じリンクを複数の記事が持ちます。直し方は、記事をリンクで引くときに、サイトと店名も条件に入れること、子の記事を親にしないことでした。「ログに同じ親の ID が何度も出たら疑う」も、教訓として残しました。

3. 2 つの商品が名乗る名前は、どちらも特定しない(9 月 5 日)

商品には、別の呼び方(別表記)を登録しておきます。「30th CELEBRATION BOX」という呼び方が、拡張パック「30th CELEBRATION」と、別の商品「30th CELEBRATION FUTURISTIC BOX」の両方の別表記に入っていました。

この名前は、どちらの商品も特定しません。AI に候補として見せると、そのとき、どちらを選んだかで結果が決まります。実際に、表示中の 25 件が、別の商品の名前で出ていました。

数えると、こういう「2 つ以上の商品が名乗る名前」は、267 組、321 件ありました。

同じ形の別の失敗もありました。店名を「@ より前」で切る処理が、「カードショップ@ほ~む。」の 3 支店を1 つの店に潰し、毎日、2 支店を「重複」として畳んでいました。

直し方は、名前で何かを特定する仕組みを作ったら、「その名前を名乗るものが 2 つ以上ないか」を数えることでした。監査を 1 本作って、0 件を保ちます。残す相手は、機械的に決まるときだけです。その名前を正式名にしている商品がちょうど 1 つなら、そこだけに残す。決まらないなら、全部から外す。足す側(入口)と、掃除する側の両方に、同じ判定を置きます。片方だけだと、外したそばから足し直されます。

分かったこと

分けた先の記事は、元の記事ではない。名前は、持ち主が 1 つのときだけ、鍵になる。

この話に出てくるもの

ほかの記録

コメント

感想や、記事の間違いの指摘は、ここへ。書くには、GitHub のアカウントが要ります。

押すと、giscus(giscus.app)と GitHub に接続します。押さなければ、接続しません。詳しくはあつかいに書いています。