あなたを悩ませる7つのREST APIデザインの誤り
三年前、私は声が震えるCTOから午前2時に電話を受けました。彼らの本番データベースが完全に消去されてしまったのです。ハッキングされたわけではありません。 disgruntled employeeではありません。ウェブクローラーによるものでした。 彼らは削除対象のユーザーのリストを返すことになっていた`/api/users/cleanup`というGETエンドポイントを持っていました。しかしこれを作成した開発者は、「クリーンアップ」の意味を「クリーンアップを実行する」と勘違いしていたのです — そのため、エンドポイントは呼び出すと実際にユーザーを削除してしまいました。検索エンジンクローラーはこのエンドポイントを発見し、インデックスに登録し、一晩で5万回も呼び出しました。すべてのユーザーレコードが消えてしまいました。 ポストモーテムには3日かかり、復旧には2週間かかりました。評判の損失は? まだ計算中です。 私は過去10年間で400以上の企業クライアントのAPIデザインをレビューしてきました。私はスプレッドシートを保持しています — はい、実際のスプレッドシートです — すべてのデザインミスとその本番環境への影響を記録しています。いくつかの誤りは数時間のコストを、いくつかは数百万のコストを伴います。すべての誤りは防止可能でした。 最悪の部分は? これらは珍しいエッジケースではないということです。瞬間的に合理的に思える基本的なデザイン決定が、時間が経つにつれて重篤な問題を引き起こします。これは、発売から6ヶ月後に午前3時に目を覚まし、逃げ道のない建築的な隅に自分を追い込んでしまったことを実感させる誤りの種類です。 私が最もよく見かける7つの誤り、なぜそれが起こるのか、そしてそれらが本番環境を悩ませる前にどのように回避するかをお話しします。これらの誤りの背後にある方法論
具体的な誤りに入る前に、私がAPIデザインの失敗をどのように分類するのかを理解する必要があります。私は単なる逸話を収集するのではなく、業界、チームサイズ、技術スタックにわたるパターンを追跡します。 今朝時点で私のスプレッドシートには847件のエントリがあります。各エントリには誤りのカテゴリー、チームサイズ、発見までの時間(誰かが問題に気付くまでの時間)、修正にかかる時間、推定ビジネス影響が含まれています。データは匿名化していますが、パターンは明確です。 誤りは3つの深刻度レベルに分かれます: レベル1: 煩わしい — これらはAPI消費者に摩擦を生じさせますが機能を破壊しません。不統一な命名規則や欠落した文書を考えてみてください。修正までの平均時間: 2-4時間。平均的なビジネス影響: 低い開発者の満足度スコア。 レベル2: 高価 — これらは大幅なリファクタリングを必要とするか、継続的なメンテナンスの負担を生じます。劣悪なバージョニング戦略や過度に結合されたエンドポイントを考えてみてください。修正までの平均時間: 2-6週間。平均的なビジネス影響: 機能リリースの遅延、増加するサポートコスト。 レベル3: 壊滅的 — これらはデータ損失、セキュリティ侵害、または完全なサービス停止を引き起こす可能性があります。データを削除するGETエンドポイントや認証バイパスの脆弱性を考えてみてください。修正までの平均時間: 1-3ヶ月(復旧を含む)。平均的なビジネス影響: 六桁から七桁。 今日取り上げる7つの誤りは、すべての3つのレベルにわたっています。いくつかは規模で対処するまでには小さく見えますが、他のものは明らかに危険ですが驚くほど一般的です。 これらの誤りが特に狡猾なのは、開発中や初期の本番展開中にはしばしば表面化しないということです。これらは、スケールを達成したとき、APIを進化させる必要があるとき、外部の消費者があなたのエンドポイントを予期しない方法で使用し始めたときに現れます。私だけが語れる物語:ページネーションの悲劇
私が個人的に目撃した最悪のAPIデザインの誤りをお話ししましょう — これは、シリーズBのスタートアップが最も大きな企業契約を失った原因です。 その会社はプロジェクト管理APIを構築しました。クリーンで、文書化が整っていて、速いものでした。彼らは、15年分のプロジェクトデータを新しいシステムに移行したいと望むFortune 500企業とのパイロットを実施しました。契約は年額230万ドルの価値がありました。 マイグレーションチームは、`/api/projects`エンドポイントを通じてデータを引き出し始めました。彼らのサンプルデータセット500プロジェクトでテストしたときはすべてが完璧に機能しました。しかし、彼らは本番環境に対してそれを実行しました:340,000プロジェクト。 そのエンドポイントはオフセットベースのページネーションを使用していました:`/api/projects?offset=0&limit=100`。標準的なものです。ただし、スケール時には、オフセットページネーションには致命的な欠陥があります:オフセットが増加するとデータベース性能が指数関数的に低下します。 レコード0-100を取得する? 速い。レコード100,000-100,100を取得する? データベースは100,000レコードをスキャンしてスキップしなければなりません。オフセット300,000に達する頃には、各リクエストが45秒かかり、タイムアウトしました。 6時間かかるはずのマイグレーションは、3日経ってもまだ実行中でした。Fortune 500のインフラチームはそれを潜在的なDDoS攻撃としてフラグ付けしました。そのスタートアップのCEOは、自らのCTOに電話をかけて、攻撃しているわけではなく、彼らのAPIが単に設計が悪かったのだと説明しなければなりませんでした。 より悪化させたのはこれでした:修正には破壊的な変更が必要でした。カーソルベースのページネーションに切り替える必要があり、すべてのクライアントが統合コードを更新する必要がありました。Fortune 500社は撤退しました。彼らはパイロット中に破壊的な変更を必要とするAPIの上に構築するリスクを取ることはできませんでした。 私は、彼らのシリーズC資金調達のデューデリジェンスで6ヶ月後に彼らのAPIをレビューしましたが、ページネーションの問題はまだ解決されていませんでした。彼らは、すでに40人の有料顧客がいて、全員のコードを更新する必要があるため、修正することを恐れていました。 これがAPIデザインの誤りの性質です — それらは硬直化します。存在すればするほど、修正が難しくなります。新しい統合が追加されるたびに、破壊的な変更ができなくなる理由が増えます。データ:本番環境で実際に壊れるもの
私は2019年以降に行った400件のAPIデザインレビューを分析しました。以下は、本番環境で問題を引き起こす原因です:| 誤りのカテゴリー | 頻度 | 発見までの平均時間 | 修正までの平均時間 | 破壊的な変更が必要か? |
|---|---|---|---|---|
| 不適切なページネーション戦略 | 67% | 4-8ヶ月 | 6-12週間 | はい(89%) |
| 不一致のエラー応答 | 82% | 2-3週間 | 3-6週間 | いいえ |
| レート制限の欠如 | 43% | 1-2ヶ月 | 2-4週間 | いいえ |
| 非冪等操作 | 38% | 3-6ヶ月 | 8-16週間 | はい(72%) |
| オーバーフェッチング/アンダーフェッチング | 91% | 1-3ヶ月 | 4-8週間 | 時々(45%) |
| 不適切なバージョニング戦略 | 56% | 6-12ヶ月 | 12-24週間 | N/A(将来の変更を防ぎます) |
| 安全でないHTTPメソッド | 12% | 1-4週間 | 1-2週間 | はい(100%) |
なぜ賢いチームがこれらの誤りを犯すのか
APIデザインについて誰も言わないことがあります:これらの誤りは無能によるものではありません。それらは、その時には合理的に見える制約の下でなされた合理的な決定によるものです。「私たちはオフセットページネーションを使用していました。なぜならそれがORMのデフォルトで与えられたからです。カーソルベースのページネーションに切り替えるとスプリントに2日追加されましたし、すでに遅れていました。問題があれば後で最適化しようと思いました。」 — フィンテックスタートアップのエンジニアリングリード、ページネーションの問題が発生する3ヶ月前これは私が繰り返し見るパターンです:チームは迅速な出荷を促進するために合理的な選択をするが、長期的なメンテナンス性を最適化するためではありません。そしてその瞬間には、しばしば正しいビジネス判断です。問題は「後で」が決して訪れないか、問題を修正するために破壊的な変更が必要になったときに訪れることです。 もう一つの一般的なパターンは、チームが内部データモデルに基づいてAPIを設計するのではなく、消費者のニーズに基づいていることです。あなたのデータベースには47のカラムを持つ`users`テーブルがありますので、あなたの`/api/users`エンドポイントはすべての47フィールドを返します。合理的に思えますよね? しかし、あなたのモバイルアプリはそのフィールドのうち5つしか必要とせず、結果的に多くのデバイスに42の不要なフィールドを送信しているのです。
「フィールドフィルタリングについて考えましたが、それは早すぎる最適化のように思えました。私たちのエンドポイントはテストで十分に速かったのです。100人のテストユーザーに対して『十分に速い』は、100,000人の本番ユーザーに対しては『受け入れがたい遅さ』になることに気づきませんでした。」 — SaaS企業のCTO、なぜ彼らのモバイルアプリが4秒のロード時間を持っているのかを説明第三のパターンは、チームがAPIの進化について考えないことです。彼らはバージョン1を設計しますが、バージョン2をどのように扱うかを考慮しません。URLまたはヘッダーにバージョン番号を含めません。どのフィールドが安定しているか、どのフィールドが変更される可能性があるかを文書化しません。すると、6ヶ月後に破壊的な変更を行う必要が出てきて、既存の統合を破壊せずに行うクリーンな方法がないことに気づくのです。 だからこそ、私は常にデザインレビューの際にチームに尋ねます:「これは変更が必要になった場合、どうなりますか?」答えが「後で考えます」であるなら、あなたは痛みを感じる準備をしています。
「RESTfulな純粋性」の仮定に挑戦する
これには人気がない意見があります:RESTの原則を厳守すると、APIは改善されるのではなく、悪化することがよくあります。 RESTの純粋主義者は、すべてのリソースには正確に1つの標準URLが必要であり、HTTPメソッドは語義を持って使用すべきであり、APIはステートレスでキャッシュ可能でなければならず、ロイ・フィールドングが彼の論文で示した他の制約すべてを守るべきだと言うでしょう。 実際には? 私がレビューした最も優れたAPIのいくつかは、その使用ケースにとって意味があるときにRESTの原則に反しています。 GitHubのAPIを取り上げてみましょう。彼らはコミットを返すエンドポイント`/repos/{owner}/{repo}/commits`を持っています。RESTfulですよね? しかし、彼らは`/search/commits`も持っていて、これも... コミットを返します。同じリソースですが、異なるURL構造です。なぜ? コミットを検索することとコミットをリストすることは根本的に異なる操作であり、標準URLのクエリパラメータに検索を押し込むことは、開発者体験を悪化させることになるからです。 また、StripeのAPIも考えてみてください。彼らは理論的にPUTであるべき冪等操作にPOSTを使用します。なぜ? POSTはHTTPクライアントによってより広くサポートされており、ヘッダーの冪等性キーはPUTと同じ保証を提供しますが、互換性の問題はありません。 ポイントはRESTが悪いのではなく、RESTはガイドラインのセットであり、宗教的な教義ではないということです。目標は直感的で、パフォーマンスが良く、メンテナンス可能なAPIを構築することです。時にはRESTの原則に従う必要があります。時には、それを意図的に破ることが必要です。「私たちはバルク更新エンドポイントをPUTにするべきかPOSTにするべきか3週間議論しました。振り返ってみると、その3週間をより良いエラーメッセージを作ることに使うべきでした。あなたのAPIが詳細なしで『500 Internal Server Error』を返すとき、誰もHTTPメソッドの純度については気にしません。」 — ヘルスケア会社のAPIアーキテクト