メインコンテンツまでスキップ

SDK設計原則 - 使いやすいSDKの特徴

このドキュメントの目的

良いSDKが持つ設計原則を理解し、SDKを使う際・作る際の判断基準を身につけることが目標です。


目次

  1. 一貫性の原則
  2. シンプルさの原則
  3. 発見可能性の原則
  4. 安全性の原則
  5. バージョニング
  6. まとめ

一貫性の原則

なぜ一貫性が重要か

┌─────────────────────────────────────────────┐
│ 一貫性のないSDK │
├─────────────────────────────────────────────┤
│ │
│ ユーザー取得: getUser(id) │
│ 商品取得: fetchProduct(productId) │
│ 注文取得: retrieveOrder(order_id) │
│ │
│ 問題: │
│ ・メソッド名がバラバラ(get/fetch/retrieve)│
│ ・引数名もバラバラ(id/productId/order_id) │
│ ・毎回調べないと使えない │
│ │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│ 一貫性のあるSDK │
├─────────────────────────────────────────────┤
│ │
│ ユーザー取得: getUser(userId) │
│ 商品取得: getProduct(productId) │
│ 注文取得: getOrder(orderId) │
│ │
│ 利点: │
│ ・パターンを覚えれば予測できる │
│ ・ドキュメントを見る回数が減る │
│ │
└─────────────────────────────────────────────┘

一貫性を保つべき領域

1. 命名規則

操作の種類と命名:
├── 取得: get〇〇(getUser, getOrder)
├── 作成: create〇〇(createUser, createOrder)
├── 更新: update〇〇(updateUser, updateOrder)
├── 削除: delete〇〇(deleteUser, deleteOrder)
└── 一覧: list〇〇s(listUsers, listOrders)

2. 引数の順序

良い例(一貫した順序):
├── createUser(userData, options)
├── createOrder(orderData, options)
└── createProduct(productData, options)

悪い例(順序がバラバラ):
├── createUser(userData, options)
├── createOrder(options, orderData) ← 順序が逆
└── createProduct(productData) ← optionsがない

3. エラーの形式

良い例(統一されたエラー構造):
{
code: "NOT_FOUND",
message: "User not found",
details: { userId: "123" }
}

悪い例(形式がバラバラ):
├── { error: "Not found" }
├── { errorCode: 404, msg: "Missing" }
└── "Error occurred"

シンプルさの原則

簡単なことは簡単に

よく使う操作は最小限のコードで実現できるべきです。

┌─────────────────────────────────────────────┐
│ 悪い例: 簡単なことが複雑 │
├─────────────────────────────────────────────┤
│ │
│ 1件のユーザーを取得するのに: │
│ │
│ config = Configuration() │
│ config.setEndpoint("https://api.example") │
│ config.setApiKey(apiKey) │
│ client = ApiClient(config) │
│ service = UserService(client) │
│ request = GetUserRequest(userId) │
│ response = service.execute(request) │
│ user = response.getData() │
│ │
│ → 8行も必要 │
│ │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│ 良い例: 簡単なことは簡単に │
├─────────────────────────────────────────────┤
│ │
│ client = Client(apiKey) │
│ user = client.getUser(userId) │
│ │
│ → 2行で完了 │
│ │
└─────────────────────────────────────────────┘

賢いデフォルト値

┌─────────────────────────────────────────────┐
│ デフォルト値の設計 │
├─────────────────────────────────────────────┤
│ │
│ 多くの場合に適切な値をデフォルトに: │
│ │
│ タイムアウト: 30秒(明示的に指定可能) │
│ リトライ回数: 3回(明示的に指定可能) │
│ ログレベル: INFO(明示的に指定可能) │
│ │
│ → 何も指定しなくても動く │
│ → 必要なときだけカスタマイズ │
│ │
└─────────────────────────────────────────────┘

段階的な複雑さ

レベル1: 基本的な使い方
└── 2-3行で動く

レベル2: カスタマイズが必要
└── オプションを指定

レベル3: 高度な制御が必要
└── 詳細な設定が可能

→ 必要に応じて深く潜れる設計

発見可能性の原則

IDEの補完を活かす

┌─────────────────────────────────────────────┐
│ 発見しやすい設計 │
├─────────────────────────────────────────────┤
│ │
│ client. │
│ ├── getUser() │
│ ├── createUser() │
│ ├── updateUser() │
│ ├── deleteUser() │
│ ├── listUsers() │
│ └── ... │
│ │
│ 「client.」と入力 → 何ができるか一覧表示 │
│ │
└─────────────────────────────────────────────┘

型の活用

┌─────────────────────────────────────────────┐
│ 型があると │
├─────────────────────────────────────────────┤
│ │
│ createUser(user: UserInput): User │
│ │
│ ・引数に何を渡すべきかわかる │
│ ・戻り値の型がわかる │
│ ・IDEが教えてくれる │
│ │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│ 型がないと │
├─────────────────────────────────────────────┤
│ │
│ createUser(data) │
│ │
│ ・dataに何を入れる? │
│ ・何が返ってくる? │
│ ・ドキュメントを見ないとわからない │
│ │
└─────────────────────────────────────────────┘

意味のある名前

悪い例:
├── process(d) ← 何をする?dとは?
├── handle(x, y) ← x, yは何?
└── doIt() ← 何をするの?

良い例:
├── sendEmail(message)
├── calculateTotal(items, taxRate)
└── validatePassword(password)

安全性の原則

フェイルセーフ設計

┌─────────────────────────────────────────────┐
│ 失敗しても安全に │
├─────────────────────────────────────────────┤
│ │
│ ネットワークエラー → 自動リトライ │
│ タイムアウト → 適切なエラーメッセージ │
│ 不正な入力 → 早期にエラー検出 │
│ │
└─────────────────────────────────────────────┘

明確なエラーメッセージ

悪い例:
"Error occurred"
"Invalid input"
"Failed"

良い例:
"API key is invalid. Please check your credentials."
"User ID must be a positive integer, got: -5"
"Connection timeout after 30 seconds. Server: api.example.com"

早期バリデーション

┌─────────────────────────────────────────────┐
│ 早期バリデーションの利点 │
├─────────────────────────────────────────────┤
│ │
│ 悪い: サーバーに送ってからエラー │
│ ├── 時間がかかる │
│ ├── ネットワーク帯域の無駄 │
│ └── エラーメッセージがわかりにくい │
│ │
│ 良い: 送る前にチェック │
│ ├── 即座にエラー検出 │
│ ├── わかりやすいメッセージ │
│ └── 無駄な通信なし │
│ │
└─────────────────────────────────────────────┘

バージョニング

セマンティックバージョニング

バージョン: X.Y.Z

X(メジャー): 破壊的変更あり
Y(マイナー): 後方互換性のある機能追加
Z(パッチ): バグ修正のみ

例:
1.0.0 → 1.0.1: バグ修正(安全にアップデート可)
1.0.0 → 1.1.0: 新機能追加(安全にアップデート可)
1.0.0 → 2.0.0: 破壊的変更(注意してアップデート)

非推奨化のプロセス

┌─────────────────────────────────────────────┐
│ 良いSDKの非推奨化プロセス │
├─────────────────────────────────────────────┤
│ │
│ 1. 非推奨の警告を出す │
│ └── 使っていると警告メッセージが出る │
│ │
│ 2. 移行先を示す │
│ └── 代わりに何を使うべきか明示 │
│ │
│ 3. 十分な移行期間を設ける │
│ └── 突然削除しない │
│ │
│ 4. メジャーバージョンで削除 │
│ └── 破壊的変更として扱う │
│ │
└─────────────────────────────────────────────┘

まとめ

4つの設計原則

┌──────────────┬────────────────────────────────┐
│ 原則 │ 内容 │
├──────────────┼────────────────────────────────┤
│ 一貫性 │ 命名、引数、エラーを統一 │
├──────────────┼────────────────────────────────┤
│ シンプルさ │ 簡単なことは簡単に │
├──────────────┼────────────────────────────────┤
│ 発見可能性 │ IDEや型で使い方がわかる │
├──────────────┼────────────────────────────────┤
│ 安全性 │ 失敗に強く、エラーが明確 │
└──────────────┴────────────────────────────────┘

SDKを評価するチェックリスト

SDKを選ぶとき、これらを確認しましょう:

  • 命名規則は一貫しているか
  • 基本的な操作は簡単にできるか
  • IDEで補完が効くか
  • エラーメッセージはわかりやすいか
  • バージョニングポリシーは明確か

次のステップ