remote-api-footguns について
Kotlin と Compose Multiplatform のリモート API 向けトラブルシューティングスキルです。本番アプリ由来の落とし穴をテーマ別に整理し、実装パターンや原因、コードの検証方法を示します。
主な機能
- 01リモート API の落とし穴を17件収録
- 02テーマ別のリファレンス
- 03障害パターンと原因の説明
- 04コード検証用コマンド
活用シーン
- 01マルチプラットフォーム HTTP クライアントの設計
- 02レスポンス解析やリモート書き込みの調査
- 03OAuth コールバックやリトライのデバッグ
- 04WebSocket のセッションハンドシェイク検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ maxrave-dev/ kotlin-footguns --skill remote-api-footgunsインストール後、Claude Code で/remote-api-footgunsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api-ok-but-ignored.mdborrowed-wire-protocol-discipline.mdcurl-logger-ktor-plugin.mdenum-normalize-over-legacy-data.mdktor-kmp-client-architecture.mdlogin-state-fans-out-to-settings.mdmonotonic-clock-offset-sync.mdnested-flag-settings-auto-disable.mdoauth-callback-not-through-nav.mdparallel-chunked-download.mdprotobuf-without-codegen-kmp.mdpublish-a-snapshot-on-taking-the-role.mdresponse-to-domain-flow.mdretry-needs-backoff-and-cap.mdstructural-defensive-parsing.mdunknown-not-a-valid-score.mdwebsocket-session-handshake-lifecycle.md詳細
リモート API の落とし穴
本番環境の Kotlin および Compose Multiplatform アプリから掘り起こした 17 個の落とし穴です。各項目はそれぞれ references/ 配下の短いファイルにまとめられています。各ファイルには、動作するパターン、落とし穴(実際にどのように失敗するか、その理由)、そしてユーザー自身のツリーに対して実行するコマンドを記載した「検証方法」セクションがあります。
このインデックスの使い方。 これから書こうとしているコード、または調査中の症状に照らし合わせて、以下の項目を確認してください。それぞれの項目には、トピックと、該当項目を確認するきっかけとなる症状が記載されています。コードや修正案を提示する前に、該当する可能性のあるファイルをすべて Read ツールで読み込んでください。各ファイルは 150 行未満です。「検証方法」のコマンドにソースプロジェクトのパスが記載されている場合は、ユーザーのツリー内の対応するパスに置き換えてください。
相互参照。 この一覧にないファイル内でバッククォートで囲まれた落とし穴名は、同じ階層にある別のスキル領域にあります。../*/references/<name>.md を glob して開いてください。
クライアント、解析、レスポンス処理
ktor-kmp-client-architecture— 1つの expect/actual がエンジンを返し、各インテグレーションがそこから独自のクライアントを構築するマルチプラットフォーム対応の HTTP スタックです。API 呼び出し用の計測機能付きクライアントと、大量ダウンロード用の意図的に機能を絞ったクライアントを併用し、コンテンツネゴシエーションは形式ごとに登録し、設定変更時にはクライアントを変更するのではなく再構築します。Kotlin Multiplatform モジュールでネットワーク機能を立ち上げるとき、プロキシ設定が一部のリクエストでは無視されるように見えるとき、大容量ファイルのダウンロードが遅く、ログが大量に出るときに役立ちます。curl-logger-ktor-plugin— 送信するすべてのリクエストを、そのまま貼り付けて使える1つの curl コマンドとしてログに記録するクライアントプラグインです。POSIX の単一引用符を使うため、引用符、ドル記号、改行を含む本文もシェルで正しく扱えます。コマンド全体を1回のログ呼び出しで出力し、秘匿化リストを備え、本文の読み取りで一度しか読めないチャネルを消費しません。アプリの外で失敗したリクエストを再実行したいとき、ログに記録されたコマンドを貼り付けても実行できないとき、ログから手作業でリクエストを再構築しないとバグを再現できないときに役立ちます。parallel-chunked-download— 1つのファイルをバイト範囲リクエストに分割し、機能を絞った HTTP クライアントで並列に実行します。各チャンクを個別の一時ファイルに保存して順番に結合し、チャネルベースのフローで進捗を報告します。範囲リクエストが安全に使える条件、チャンクの適切なサイズ、失敗時にファイル全体ではなく1つのチャンクだけを再試行する理由も説明します。大容量ダウンロードが回線速度に見合わないとき、一時的な問題の後にダウンロードが最初から再開するとき、進捗バーが完了直前で止まるときに役立ちます。structural-defensive-parsing— 形状が変わるレスポンスの読み取り方法です。各フィールドを位置ではなくペイロード自体が宣言するマーカーで分類し、フィルタリングマップをデータ損失として扱って除外件数を記録し、パースに失敗したときにプレースホルダーで代用せず、複合文字列を安定した末尾からパースします。あるロケールでは動作するパーサーが別のロケールでは動作しないとき、リストの項目数がソースの表示より少ないとき、入力した覚えのない場所に作り物の値が現れるときに役立ちます。response-to-domain-flow— リモートレスポンスが通過する5つの段階を説明します。インテグレーションごとのサービスモジュールに置くトランスポートモデル、純粋なパーサーレイヤー、ドメインモデル、結果エンベロープ、そしてコレクションです。1つのソースの障害が他へ波及しないよう各インテグレーションを独立したモジュールにするルールと、トランスポート型を画面に持ち込まない配置ルールも扱います。2つ目のリモートソースを追加するとき、UI ファイルがレスポンスクラスをインポートし始めたとき、レスポンスの形状が変わった後に画面のスピナーが回り続けるときに役立ちます。api-ok-but-ignored— リモートへの書き込みは「ok」と応答しながら、送信した内容を破棄していることがあります。そのことは、成功レスポンスに付随する二次フィールドにしか示されない場合があります。「受け付けられたが破棄された」状態を独立した結果としてモデル化し、すべての書き込みでそのフィールドを読み取り、破棄を明確にログへ記録します。送信のたびに成功が報告されるのに、データが相手側に現れないときに役立ちます。unknown-not-a-valid-score— パースに失敗したときのフォールバックには、合法な値の範囲外にあるセンチネルを使うか、型で表現します。成功時にも生成され得る値を使ってはいけません。「不明」を独立した問いとして公開します。値がどこから来たかによって意味が異なるとき、プレースホルダーが画面に表示されるとき、利用側で値が未設定なのか測定済みなのか判別できないときに役立ちます。enum-normalize-over-legacy-data— リモートソースが宣言する型マーカーを読み取る方法です。古いアプリバージョンが保存した行にはアプリ独自のラベルが入っているため、比較の前に必ず正規化します。null はデフォルトではなく「ソースが指定していない」として扱い、呼び出し側が情報の有無で分岐できるよう is-known 判定を公開し、マイグレーションではなく書き戻しによって古い行を修正します。保存された型の列に複数の表記が混在するとき、項目が誤った種類として扱われるとき、過去の値を修正するためにデータベースマイグレーションを追加する前に役立ちます。
認証、再試行、アカウント状態
oauth-callback-not-through-nav— ログインコールバックで返されるトークンをセッション状態に直接渡し、保存されたセッションを監視してログイン画面を閉じるようにします。トークンをナビゲーション経由で渡すと、2つ目のログイン画面が開き、ログイン後に閉じる際に誤った画面が閉じられます。ブラウザーベースのログインに成功したのに、ユーザーがログイン画面から先に進めない場合に使用します。retry-needs-backoff-and-cap— すべての再接続ループに指数バックオフ、上限、再試行しない失敗の分類、ライフサイクルのゲートを設けます。さらに、その機能にヘルスシグナルを追加します。そうしないと、ひそかに失敗したまま何か月も動かなくなります。バックグラウンド接続でバッテリーが消耗する場合、不正な認証情報によって再接続が延々と繰り返される場合、または連携機能がいつの間にか動作しなくなった場合に使用します。login-state-fans-out-to-settings— ログアウト時には、ログアウト処理の要所で、ログイン状態に依存するすべての設定をリセットする必要があります。そうしないと、認証されていないサービスのスイッチがオンのままになり、何もせずに動かなくなったり、毎回エラーになったりします。機能のトグルがオンのまま戻らない、オフにできない、または無効になった認証情報で動作し続ける場合に使用します。nested-flag-settings-auto-disable— 親条件によって有効になる子トグルでは、親の現在値を基準に自動無効化の効果を適用し、条件が満たされないときは非表示ではなくグレーアウトし、利用側でも再度ゲートする必要があります。そうしないと、子トグルがオンのままになり、ユーザーがオフにできなくなります。設定スイッチがオンのまま戻らない、有効と表示されたままグレーアウトする、または前提条件がなくなった後も動作し続ける場合に使用します。
リアルタイムセッションとワイヤープロトコル
monotonic-clock-offset-sync— ping/pong の往復から相手のクロックオフセットを推定します。半分にする前に相手側の処理時間を差し引き、各サンプルを観測済みの最良の往復時間に照らして重み付けし、ローカルの時刻ソースが単調増加することを必須とします。推定値がまだ利用できない間は補正前の値にフォールバックします。複数のデバイスがストリームの位置を一致させる前に時刻を一致させる必要がある場合、ネットワークの混雑でグループ内にずれが生じる場合、またはデバイスの時刻調整後に位置補正が急に跳ねる場合に使用します。protobuf-without-codegen-kmp— 通常のデータクラスにフィールド番号を注釈することで、コード生成レイヤーなしで共有 Kotlin から protobuf を扱います。生成されたエンコーダーとバイト列を一致させるエンコーダー設定、バイト配列フィールドに必要な等価性のオーバーライド、同等性を固定する適合性テストを含みます。スキーマ駆動のプロトコルを JVM だけでなくすべてのターゲットで動作させる必要がある場合、ネストされたフィールドが存在しないメッセージのエンコードで例外が発生する場合、またはラウンドトリップテストは成功するのに実際の相手がフレームを拒否する場合に使用します。borrowed-wire-protocol-discipline— 他者が定義したプロトコルを実装するためのルールです。名前を変更せず、順序を入れ替えず、スキーマにない定数は推測せずに相手側の実装から読み取り、未知のメッセージ型は例外を投げず null にデコードし、ネゴシエートした機能は縮小しても拡大しません。制御できない実装とクライアントを相互運用する必要がある場合、接続は確立するのにその先に進まない場合、または新しいバージョンの相手によってセッションが壊れる場合に使用します。websocket-session-handshake-lifecycle— WebSocket セッションを開始し、終了するまでの手順です。応答はリーダー経由で返るため最初のメッセージより先にリーダーを起動し、タイムアウト付きの deferred でハンドシェイクを確定し、キャンセル不可のコンテキストで close フレームを送信し、イベントバッファーはイベントを破棄せずに一時停止します。ソケットは接続するのにセッションが利用可能にならない場合、意図的に切断しても相手側で接続中のままになる場合、またはトラフィックの集中後にクライアント間で同期がずれる場合に使用します。publish-a-snapshot-on-taking-the-role— 共有セッションのすべてのパブリッシャーは変更を契機に動作するため、実行中の参加者がパブリッシャーの役割を担っても何も送信されず、グループが沈黙したままになることがあります。ソースになったときと、新しいメンバーが参加したときに、完全なスナップショットを公開します。誰かがトランスポートに触れるまでセッションが空のままの場合、後から参加したユーザーに何も見えない場合、または状態の監視はすべて正しく見えるのにグループが何も認識しない場合に使用します。