ページングAPIで「全件取れたつもり」が4%だった — 静かな取りこぼしの検出法
limit を無視して常に20件返すAPIに対し offset を要求limit分進めていたため6,902件中280件しか取れていなかった。テストもCIも緑だった理由と、本番の意地悪さを再現するモックの書き方。
約1.2万トークンの節約 (API料金換算で約17円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「ページングAPIで「全件取れたつもり」が4%だった — 静かな取りこぼしの検出法」は、開発プロセスカテゴリのAI指示書(MDファイル)です。limit を無視して常に20件返すAPIに対し offset を要求limit分進めていたため6,902件中280件しか取れていなかった。テストもCIも緑だった理由と、本番の意地悪さを再現するモックの書き方。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約1.2万トークン(API料金換算で約17円)・83%のトークンを節約できます。
- カテゴリ
- 開発プロセス
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約1.4万トークン
- この巻物使用時
- 約2,400トークン
- 節約量
- 約1.2万トークン (約17円)
- 更新日
- 2026-09-10
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/api-4/raw を読み込んで、この指示書どおりに実装して"
中身
ページング API で「全件取れているつもり」で大半を取りこぼす事故と、その検出方法
1. 何が起きたか
認証必須の社内 <Wiki> から全ページを取得するツールを書きました。総件数は <一覧エンドポイント> が totalCount として返しており、6,902 件です。
実装は「1 回のリクエストで 500 件ずつ取得し、offset を 500 ずつ進める」という素直なループでした。単体テストは全て緑、CI も緑、ローカル実行も緑。
ところが本番で実行したところ、実際に取れていたのは 280 件だけでした。取得率にして 約 4%。エラーは一切出ておらず、ツールは正常終了していました。
2. 原因
<一覧エンドポイント> は limit パラメータを完全に無視し、常に 20 件だけ返す仕様でした。実測値は次のとおりです。
| 要求 | 実際の返却件数 |
|---|---|
limit=100&offset=0 | 20 |
limit=500&offset=0 | 20 |
limit=100&offset=1000 | 20 |
一方、実装側は offset += limit(=500)で進めていました。つまり 1 回のリクエストごとに 480 件を読み飛ばしていたことになります。
計算すると 6902 / 500 ≒ 14 反復 × 20 件 = 280 件 となり、実測値と完全に一致します。取得漏れは「たまたま」ではなく、この構造から必然的に生じていました。
3. なぜテストで捕まらなかったのか
ここが本題です。テスト用のモック HTTP サーバが、「要求された limit の件数をきちんと返す」お行儀のよい実装になっていました。
そのため、テストは「実装が正しいか」ではなく 「モックが想定どおり振る舞うか」を検証していただけでした。本番の意地悪な挙動(limit を無視する)を再現できていなかったので、壊れているのに全部緑、という状態が成立していました。
モックが行儀よすぎると、テストは実装を検証せず自分の前提を検証しているだけになります。
4. 修正
offset += 実際に返ってきた件数にする。要求したlimitは信用しない- 返却 0 件で終了する。加えて進捗が止まったら中断するガードを入れる(同じ結果を返し続けるサーバで無限ループになるため)
- 取得済み ID の Set で重複排除する(一覧が更新順などの可変順序だと、同じ項目が別のページで再登場する)
- 取得後に必ず
totalCountと突き合わせ、一致しなければ警告を出す。今回これで気づけました。数を数えない実装は、静かに壊れたまま動き続けます
擬似コードにすると次のようになります。
seen = Set()
offset = 0
while true:
batch = fetch(offset, limit=500) # limit は「お願い」にすぎない
if batch.length == 0:
break
new_items = batch.filter(x => not seen.has(x.id))
if new_items.length == 0:
break # 進捗が止まった → 中断
for x in new_items:
seen.add(x.id)
offset += batch.length # 要求値ではなく実返却件数で進める
if seen.size != totalCount:
warn("取得件数が総数と一致しません: {seen.size} / {totalCount}")
5. 再発防止のモック設計
- モックは本番の意地悪さを再現する。「
limitを無視して常に N 件返す」モックを 1 本用意する - 総数は
20で割り切れない数(例: 130)にして、端数の扱いも検証する - 検証項目は次の 3 つ
- 収集件数 ==
totalCount - 重複 0 件
- 期待する ID の全集合と一致
- 収集件数 ==
- 修正前の実装でそのテストが落ちることまで必ず確認する(落ちないテストは意味がない)
- 「並び順が動くサーバ」のモックも用意し、複数パスで収束することを確認する
6. チェックリスト
-
offsetは実際に返ってきた件数で進めている(要求したlimitで進めていない) - 返却 0 件で終了し、進捗が止まったら中断するガードがある
- 取得済み ID の Set で重複排除している
- 取得後に
totalCountと突き合わせ、不一致なら警告を出している - モックに「
limitを無視して常に N 件返す」意地悪なケースがある - 総数が割り切れない値(端数あり)でテストしている
- 修正前の実装でそのテストが落ちることを確認済み
7. 一般化した教訓
- ページング実装は「サーバが仕様どおり振る舞う」前提で書くと静かに壊れる
- 総数と突き合わせない取得処理は、壊れても気づけない
- モックが行儀よすぎると、テストは実装を検証せず自分の前提を検証しているだけになる
よくある質問
+「ページングAPIで「全件取れたつもり」が4%だった — 静かな取りこぼしの検出法」とは何ですか?
limit を無視して常に20件返すAPIに対し offset を要求limit分進めていたため6,902件中280件しか取れていなかった。テストもCIも緑だった理由と、本番の意地悪さを再現するモックの書き方。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約1.4万トークンかかりますが、この巻物を使えば約2,400トークンで済みます。差し引き約1.2万トークン(API料金換算で約17円)・83%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
夜間ジョブ異常を通知で終わらせず自動修復→AI修理PR→人へ引き渡す閉ループ
監視の『検知して通知』の後段に、決定的Playbook→AIコーダーの隔離worktree修理PR→持ち越し→人への3要素引き渡し、を足す実装指示書。argvで指示を渡すな等の実測の落とし穴つき
ドキュメント駆動開発プロセス CLAUDE.md — 作るものを固めてから書かせる
「AIが暴走して意図と違うものを作る」を根絶する開発プロセス指示書。UI仕様→機能設計→実装の順をAIに強制し、1ファイルごとに承認ゲートを挟む。受託開発・チーム開発向け。
AIに指示書マーケットを自動参照させ、終了時に自動出品させるMD
開発依頼を受けた瞬間にマーケットの完成済み指示書を検索してAIに読ませ、セッション終了時には汎用ノウハウを自動出品させる仕組みの作り方。全台配布・秘密情報スキャン・実際に踏んだ配布バグ3つの回避込み。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア