修正済みのコードが、直したはずの欠陥を別の形で再生産していた——それをわざと穴を探す2周目のレビューが捕まえた事例を整理する。
綴Photo のモバイル版には、カメラロールから写真を1枚選ぶピッカー画面があります。レビューで分かったのは、この画面が先頭300枚しか出していなかったこと。スクロールしても「もっと見る」も無く、300枚目でグリッドが物理的に終わります。それ以上があることを示す表示すら出ません。
エラーが出るわけでも、空っぽなわけでもありません。ちゃんと写真は並んでいて、ただ途中で黙って終わっています。「カメラロールから選ぶ」と言っておいて300枚で打ち切る、気づかれにくい類のUX欠陥だ。これをページング(段階ロード)に直した——つもりが、直した実装が別の形で同じ「静かな天井」を作っていた。そこが今回いちばんの学びだった。
元の問題: エラーでも空白でもない「静かな天井」
写真を取ってくる部分が、こうなっていた。
// アルバムの先頭300枚だけを取得する(それ以上は取りに行かない)
return album.getAssetListRange(start: 0, end: 300);この手の欠陥が怖いのは、気づかれにくいことです。エラーなら気づきます。空っぽなら気づきます。でも「ちゃんと表示されているけど、実は途中で切れている」は、見た目が正常なので誰も疑いません。沈黙の天井、と呼んでおく。
直した方向: スクロールで段階ロード
300枚固定をやめて、末尾近くまでスクロールしたら次のページを継ぎ足す形に変えた。末尾には小さなインジケータ(読み込み中マーク)を置いて「まだ続く」を見せる(無言で終端させない、が狙い)。これで先頭300枚の天井は無くなった。
直したつもりが再生産していた沈黙
ここで2周目のレビューにかけた。1周目で「直す箇所」を見つけ、直したあと、今度はわざと穴を探す目で修正そのものを見直すパスだ。そこで出てきた指摘がこれだった。
追加ロードが失敗したとき、末尾のスピナーが回り続けるだけで、エラーも再試行ボタンも出ません。ユーザーが末尾で指を止めていると、永遠に回るスピナーだけが残ります。
// isLoadingPage: ページ取得中かどうか(失敗しても false に戻る)
// hasMore: まだ続きがあるかどうか(失敗では変えていない = true のまま残る)
// loadError: 失敗を記録する変数(ここにセットする)
catch (e) {
loadError = e; // ← エラーは記録した
isLoadingPage = false; // ← 「取得中」は終わりにした
// hasMore は変えていない = 「まだ続く」という状態のまま
// → フッターは「続きがある・ロード中でない」= スピナーを回し続ける(出口が無い)
}
// なぜこうなりやすいか:
// ページング実装は「成功する前提」で書かれることが多い。
// hasMore の更新を「全件取り終わった時」だけ想定すると、
// 失敗した時の分岐を書き忘れる——それが沈黙の出口になる。直し直した方向
フッターの状態を、ロード中・失敗・終端で分けた。失敗中はスクロールで自動再試行しない——指を動かすたびに失敗を繰り返すと、それはそれで別の沈黙になるので、ボタンを押されたときだけやり直す。
// フッターの出し分け:失敗をスピナーで終わらせず、必ず「終わり方」を見せる
if (hasLoadMoreError) return Footer.retry; // 再試行ボタン
if (isLoadingMore || hasMore) return Footer.spinner; // ロード中/まだ続く
return Footer.none; // 全部出したポイントは「失敗をスピナーで終わらせない」こと。ロード中の表示は「成功するまでの一瞬」を暗黙に前提にしがちだが、失敗したときに何を見せるかまで決めておかないと、スピナーがそのまま沈黙の天井になります。
学びポイント
- 沈黙系の欠陥(エラーでも空白でもなく、ただ途中で終わる/終わらない)は一番気づかれにくいです。だから潰したつもりでも形を変えて戻ってきます
- ローディング表示は「成功するまでの一瞬」を前提にしがち。失敗したときに何を見せるかまで設計しないと、スピナーが新しい沈黙になります
- 失敗中の自動再試行(スクロールのたびに再挑戦)は、それ自体が別の沈黙を生みます。明示的な「再試行」操作を待つ方が、状態が見えます
- 「直した」と「直っている」は別。直した結果を、最初の欠陥と同じ目でもう一度見る2周目が効きます。特に自動テストが書きにくいUI部分では、わざと穴を探すレビューが頼りになります
おわりに
沈黙の天井は、場所を変えて何度でも生えます。共通していたのは「ユーザーに『ここで終わり』『失敗した』を告げていない」ことだった。表示が途切れる/止まること自体より、それを黙ってやることが問題です。
1周目は打ち切りを見つけ、2周目はその修正が同じ穴を別の形で開けているのを見つけた。直す目と疑う目は別だった。
この記事は2026年6月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。