8月25日、Sean Goedecke氏が「Everything I know about good API design」と題したブログ記事を公開し、話題を呼んでいる。この記事では、API設計の難しさと面白さを豊富な事例とともに掘り下げ、実務で役立つ具体的な指針を提示している。以下に、その内容を紹介する。
API設計は「退屈」であるべき
APIは、設計者にとっては時間をかけて練り上げる複雑なプロダクトだが、利用者にとっては単なる手段にすぎない。利用者は「APIを使うこと」そのものを目的としていない。彼らは「SMSを送信する」「AIモデルを呼び出す」といった別のゴールを達成するためにAPIを使う。だからこそ「良いAPIは退屈であるべきだ」と断言されている。
利用者がドキュメントを読む前からおおよその挙動を予測できるくらい、直感的で馴染みやすいことが理想である。逆に「面白いAPI」は概して悪いAPIだ。APIの仕組みを考える時間は、利用者にとって無駄でしかないからだ。
「互換性を壊さない」という神聖な義務
APIの厄介な点は、一度公開すると変更がきわめて難しいことである。公開後に利用者が依存し始めた時点で、後方互換性を壊す変更は即座に数百、数千のソフトウェアを壊しかねない。そのため、API設計者には「既存利用者を壊さない」という強い責任が課せられる。
Linuxカーネル開発における有名なスローガン「WE DO NOT BREAK USERSPACE」(ユーザー空間を壊すべからず)は、この姿勢を象徴する。HTTP仕様の「referer」ヘッダーが綴り間違いのまま修正されない(正しくは「referrer」)のも、まさにこの原則を体現している。
バージョニングは「必要悪」
それでもどうしても破壊的変更が必要になる場合がある。そのとき頼るのが「バージョニング」である。
最も単純なのはURLに/v1/
を含める方式で、OpenAIの/v1/chat/completions
がその例だ。Stripeのようにヘッダーでバージョンを指定させる方式も存在する。これにより既存利用者は従来のバージョンを使い続けられ、新規利用者は新しいバージョンを利用できる。
しかし、バージョニングは決して万能ではない。ドキュメントは煩雑化し、利用者は「自分が今どのバージョンを見ているのか」を常に意識する必要がある。開発側にとっても、エンドポイント数が膨れ上がり、保守やテストのコストは雪だるま式に増える。翻訳レイヤーを導入しても、結局どこかで条件分岐が漏れ、現場のコードに影響を与える。
結論は明快だ。バージョニングは「最後の手段」としてしか使うべきではない。
成否を決めるのはAPIではなく製品
APIの品質は重要だが、最終的に成否を決めるのは製品そのものである。
OpenAIのAPIが注目されるのは「推論機能を提供しているから」であり、TwilioのAPIが使われるのは「SMS送信ができるから」である。API単体が選ばれるのではなく、背後の機能に価値があるからこそ利用される。
FacebookやJiraのAPIは「悪名高いほど扱いづらい」が、それでも世界中で利用され続けている。理由は単純で「それでも利用者が必要だから」である。裏を返せば、どれだけ優れたAPIを設計しても、価値のない製品では誰も使わないということだ。
製品設計の歪みはAPIに現れる
APIは製品のリソース設計をそのまま反映する。設計が不自然なら、その不自然さはAPIに露骨に現れる。
記事では「コメントをリンクリストで保存するブログシステム」という極端な例が挙げられている。この設計に基づくAPIは、次のような不自然な形を取るだろう。
GET /comments/1 -> { id: 1, body: "...", next_comment_id: 2 }
あるいは、入れ子構造で延々とコメントを返す形式にもなり得る。
GET /comments -> {
body: "...",
next_comment: {
body: "...",
next_comment: { ... }
}
}
コメント数が数千件に及ぶ場合、実用に耐えない。この場合、非同期ジョブを発行し、進捗を確認して結果を取得するような複雑な設計にならざるを得ない。こうした「製品設計上の歪み」がそのままAPIに噴出することで、利用者は本来知る必要のない内部実装を理解しなければならなくなる。これが「悪いAPI」が生まれる典型的なパターンである。
実践的な設計の指針
記事では、具体的に守るべき設計ポイントも示されている。
- 認証:まずは長期有効なAPIキーを提供する。OAuthなど高度な仕組みも必要だが、最初のスクリプトが動かせることが最重要である。非エンジニアの利用者も多いため、複雑な認証は障壁になる。
- 冪等性(idempotency):リトライによる重複を避けるため、リクエストには「idempotency key」を設けるべきである。決済や医療など高リスク領域では特に必須だ。
- 安全性とレート制限:APIはUIと違い「コードの速度」で叩けるため、無制限に利用されれば容易にシステムが停止する。Zendeskで通知APIを使ってチャットシステムを構築し、サーバーがダウンした事例のように、想定外の使われ方は必ず発生する。したがって、厳格なレート制限や緊急停止機構を備える必要がある。
- ページネーション:大量データにオフセット方式を使うとスケールしない。カーソルベース方式を用いることで、数十万件を超えるデータでも一定の速度で取得できる。レスポンスには
next_page
を含め、利用者が自然に次のデータを追えるようにする。 - オプションフィールドとGraphQL:高コストなフィールドはリクエスト時に明示的に指定させるべきである。GraphQLは柔軟性を提供する一方で、学習コストやキャッシュの難しさがあり、導入は慎重にすべきだ。
- 内部API:社内専用APIは利用者数が限られ、破壊的変更も制御可能である。そのため設計の自由度は高いが、冪等性や安全性といった原則は依然として重要だ。
まとめ
- APIは「退屈」でなければならない。利用者が考え込む時間を奪ってはいけない
- 互換性を壊すことは最大の罪であり、公開APIでは絶対に避けるべきである
- バージョニングは混乱とコストを招くため、やむを得ない場合にのみ使う
- APIの利用を決めるのは製品そのものの価値であり、API単体ではない
- 製品設計が歪んでいれば、APIも必然的に悪くなる
- 認証、冪等性、レート制限、ページネーション、オプション設計は必須の要素である
- GraphQL導入は利点と負担を天秤にかけ、必要な場面に限定する
- 内部APIは事情が異なるが、品質を軽視すべきではない
詳細はEverything I know about good API designを参照していただきたい。