公開コンテンツを扱う小さなツールを作っていると、「できない」とはっきり書くことが設計の一部になる、という場面が何度もあります。
私は普段の仕事とは別に、小さなウェブツールをいくつか作っています。直近のものは TikStories という、公開されている TikTok アカウントのストーリーを見るためのビューアです。この記事は、その実装そのものより、「取得できる範囲」をどう決めて、それをどうユーザーに伝えるかという話です。同じ形の問題は、公開 API やスクレイピングを扱うツールなら大抵ぶつかると思います。
境界は技術ではなく仕様で決まっている
まず前提として、非公開アカウントのストーリーは取得できません。これは「難しい」ではなく「不可能」です。非公開アカウントのコンテンツは承認済みフォロワーにしか配信されないので、取りに行く先の公開コピーがそもそも存在しません。
ここを曖昧にしたまま作ると、実装中に「もう少し頑張れば取れるのでは」という方向に時間を溶かすことになります。実際、この分野には「非公開アカウントを見られる」と謳うページが大量にあります。動いているものは一つもありません。良くて空箱、悪ければログイン情報の収集です。
技術的な境界ではなく仕様上の境界だと最初に認識しておくと、設計が一段シンプルになります。取れないものは機能一覧から消え、代わりに「取れないと明示する」という UI 要件が増えるだけです。
有効期限があるデータは「経過」ではなく「残り」を出す
TikTok のストーリーは投稿から 24 時間で消えます。ここで UI に出す値の選択肢が二つあります。
- 投稿からの経過時間(「6時間前」)
- 期限までの残り時間(「あと18時間」)
同じ情報の裏表ですが、ユーザーが知りたいのは後者だけです。「6時間前」と表示された場合、読み手は「この形式の有効期限は24時間」という前提知識を持っていて、かつ引き算をして初めて意味がわかります。
私が他のストーリービューアを調べたときに一番違いを感じたのがここで、多くは時間に関する情報を何も出していませんでした。結果として、残り20時間のストーリーと残り20分のストーリーが画面上で完全に同じ見た目になります。
一般化すると、TTL のあるデータは締切側を基準に表示する。一時的な共有リンク、期限付きトークン、その日の終わりにリセットされるステータス、どれも同じです。ユーザーは締切側に立っています。
「結果なし」が例外ではなく通常ケースになる設計
もう一つ、作ってから気づいたことがあります。ストーリーは、ほとんどの時間「存在しない」。
ストーリー投稿はバースト的な行動で、ある晩に数本投稿されたあとは何日も何もない、というのが普通です。つまり「アクティブなストーリーが0件」という分岐は、網羅性のために書くエッジケースではなく、最も頻繁に通るパスです。
ここで「ストーリーが見つかりません」とだけ出す実装は、統計的にはほぼ白い画面を出すツールになります。TikStories では代わりに、ストーリーがないときは同じアカウントの最近の動画一覧にフォールバックします。ユーザーの本来の問い(このアカウントは何を投稿しているのか)には、ストーリーがなくても答えられるからです。
空状態を設計するときの実務的な問いは「結果0件をどう表示するか」ではなく、**「ユーザーが本当に知りたかったことに、別の形で答えられないか」**だと思っています。
入力は緩く、能力の説明は厳密に
ユーザーが貼り付けてくる文字列は一種類ではありません。素のユーザー名、@ 付き、プロフィールの URL、そしてモバイルの共有シートが吐くトラッキングパラメータ付きの URL。
最後のものをそのまま使うと二つ問題があります。アカウントではなく「その共有イベント」に紐づく文字列をキーにしてしまうこと、そして他人の端末由来の識別子を自分のログに写し取ってしまうことです。なので入力は正規化してからハンドルだけを取り出します。数行のコードで、バグと気まずさの両方が消えます。
一方で、入力に寛容であることと、できることを曖昧にすることは別です。この二つを混同すると「何を貼っても受け付けるが、何も返ってこない」ツールになります。
TikStories の場合、できないことは4つで、すべて恒久的です。非公開アカウントは開けない。期限切れのストーリーは復元できない(アーカイブを持っていません)。自分のストーリーを誰が見たかは表示できない(そのリストはアプリ内にしか存在しません)。そしてダウンローダーではなく、あくまでビューアです。
まとめ
公開データを扱うツールの設計で効いたのは、結局この3つでした。
- 取得できる境界は仕様側で決まっている。技術で突破する対象ではないので、機能一覧から外して「できない」と書く
- 期限付きデータは残り時間を出す
- 空の結果が通常ケースなら、そこに別の答えを置く
どれも高度なことではありませんが、実装に潜っていると出てこなくなる種類の問いです。