SDK設計原則 - 使いやすいSDKの特徴
このドキュメントの目的
良いSDKが持つ設計原則を理解し、SDKを使う際・作る際の判断基準を身につけることが目標です。
目次
一貫性の原則
なぜ一貫性が重要か
┌─────────────────────────────────────────────┐
│ 一貫性のない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で補完が効くか
- エラーメッセージはわかりやすいか
- バージョニングポリシーは明確か
次のステップ
- SDK実装パターン で具体的な実装パターンを学ぶ