$npx -y skills add ancoleman/ai-design-components --skill designing-sdksDesign production-ready SDKs with retry logic, error handling, pagination, and multi-language support. Use when building client libraries for APIs or creating developer-facing SDK interfaces.
| 1 | # SDK Design |
| 2 | |
| 3 | Design client libraries (SDKs) with excellent developer experience through intuitive APIs, robust error handling, automatic retries, and consistent patterns across programming languages. |
| 4 | |
| 5 | ## When to Use This Skill |
| 6 | |
| 7 | Use when building a client library for a REST API, creating internal service SDKs, implementing retry logic with exponential backoff, handling authentication patterns, creating typed error hierarchies, implementing pagination with async iterators, or designing streaming APIs for real-time data. |
| 8 | |
| 9 | ## Core Architecture Patterns |
| 10 | |
| 11 | ### Client → Resources → Methods |
| 12 | |
| 13 | Organize SDK code hierarchically: |
| 14 | |
| 15 | ``` |
| 16 | Client (config: API key, base URL, retries, timeout) |
| 17 | ├─ Resources (users, payments, posts) |
| 18 | │ ├─ create(), retrieve(), update(), delete() |
| 19 | │ └─ list() (with pagination) |
| 20 | └─ Top-Level Methods (convenience) |
| 21 | ``` |
| 22 | |
| 23 | **Resource-Based (Stripe style):** |
| 24 | |
| 25 | ```typescript |
| 26 | const client = new APIClient({ apiKey: 'sk_test_...' }) |
| 27 | const user = await client.users.create({ email: 'user@example.com' }) |
| 28 | ``` |
| 29 | |
| 30 | Use for APIs <100 methods. Prioritizes developer experience. |
| 31 | |
| 32 | **Command-Based (AWS SDK v3):** |
| 33 | |
| 34 | ```typescript |
| 35 | import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3' |
| 36 | await client.send(new PutObjectCommand({ Bucket: '...' })) |
| 37 | ``` |
| 38 | |
| 39 | Use for APIs >100 methods. Prioritizes bundle size and tree-shaking. |
| 40 | |
| 41 | For detailed architectural guidance, see `references/architecture-patterns.md`. |
| 42 | |
| 43 | ## Language-Specific Patterns |
| 44 | |
| 45 | ### TypeScript: Async-Only |
| 46 | |
| 47 | ```typescript |
| 48 | const user = await client.users.create({ email: 'user@example.com' }) |
| 49 | ``` |
| 50 | |
| 51 | All methods return Promises. Avoid callbacks. |
| 52 | |
| 53 | ### Python: Dual Sync/Async |
| 54 | |
| 55 | ```python |
| 56 | # Sync |
| 57 | client = APIClient(api_key='sk_test_...') |
| 58 | user = client.users.create(email='user@example.com') |
| 59 | |
| 60 | # Async |
| 61 | async_client = AsyncAPIClient(api_key='sk_test_...') |
| 62 | user = await async_client.users.create(email='user@example.com') |
| 63 | ``` |
| 64 | |
| 65 | Provide both clients. Users choose based on architecture. |
| 66 | |
| 67 | ### Go: Sync with Context |
| 68 | |
| 69 | ```go |
| 70 | client := apiclient.New("api_key") |
| 71 | user, err := client.Users().Create(ctx, req) |
| 72 | ``` |
| 73 | |
| 74 | Use context.Context for timeout and cancellation. |
| 75 | |
| 76 | ## Authentication |
| 77 | |
| 78 | ### API Key (Most Common) |
| 79 | |
| 80 | ```typescript |
| 81 | const client = new APIClient({ apiKey: process.env.API_KEY }) |
| 82 | ``` |
| 83 | |
| 84 | Store keys in environment variables, never hardcode. |
| 85 | |
| 86 | ### OAuth Token Refresh |
| 87 | |
| 88 | ```typescript |
| 89 | const client = new APIClient({ |
| 90 | clientId: 'id', |
| 91 | clientSecret: 'secret', |
| 92 | refreshToken: 'token', |
| 93 | onTokenRefresh: (newToken) => saveToken(newToken) |
| 94 | }) |
| 95 | ``` |
| 96 | |
| 97 | SDK automatically refreshes tokens before expiry. |
| 98 | |
| 99 | ### Bearer Token Per-Request |
| 100 | |
| 101 | ```typescript |
| 102 | await client.users.list({ |
| 103 | headers: { Authorization: `Bearer ${userToken}` } |
| 104 | }) |
| 105 | ``` |
| 106 | |
| 107 | Use for multi-tenant applications. |
| 108 | |
| 109 | See `references/authentication.md` for OAuth flows, JWT handling, and credential providers. |
| 110 | |
| 111 | ## Retry and Backoff |
| 112 | |
| 113 | ### Exponential Backoff with Jitter |
| 114 | |
| 115 | ```typescript |
| 116 | async function retryWithBackoff<T>(fn: () => Promise<T>, maxRetries: number): Promise<T> { |
| 117 | let attempt = 0 |
| 118 | |
| 119 | while (attempt <= maxRetries) { |
| 120 | try { |
| 121 | return await fn() |
| 122 | } catch (error) { |
| 123 | attempt++ |
| 124 | if (attempt > maxRetries || !isRetryable(error)) throw error |
| 125 | |
| 126 | const exponential = Math.min(1000 * Math.pow(2, attempt - 1), 10000) |
| 127 | const jitter = Math.random() * 500 |
| 128 | await sleep(exponential + jitter) |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | function isRetryable(error: any): boolean { |
| 134 | return ( |
| 135 | error.code === 'ECONNRESET' || |
| 136 | error.code === 'ETIMEDOUT' || |
| 137 | (error.status >= 500 && error.status < 600) || |
| 138 | error.status === 429 |
| 139 | ) |
| 140 | } |
| 141 | ``` |
| 142 | |
| 143 | **Retry Decision Matrix:** |
| 144 | |
| 145 | | Error Type | Retry? | Rationale | |
| 146 | |------------|--------|-----------| |
| 147 | | 5xx, 429, Network Timeout | ✅ Yes | Transient errors | |
| 148 | | 4xx, 401, 403, 404 | ❌ No | Client errors won't fix themselves | |
| 149 | |
| 150 | ### Rate Limit Handling |
| 151 | |
| 152 | ```typescript |
| 153 | if (error.status === 429) { |
| 154 | const retryAfter = parseInt(error.headers['retry-after'] || '60') |
| 155 | await sleep(retryAfter * 1000) |
| 156 | } |
| 157 | ``` |
| 158 | |
| 159 | Respect `Retry-After` header on 429 responses. |
| 160 | |
| 161 | See `references/retry-backoff.md` for jitter strategies, circuit breakers, and idempotency keys. |
| 162 | |
| 163 | ## Error Handling |
| 164 | |
| 165 | ### Typed Error Hierarchy |
| 166 | |
| 167 | ```typescript |
| 168 | class APIError extends Error { |
| 169 | constructor( |
| 170 | message: string, |
| 171 | public status: number, |
| 172 | public code: string, |
| 173 | public requestId: string |
| 174 | ) { |
| 175 | super(message) |
| 176 | this.name = 'APIError' |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | class RateLimitError extends APIError { |
| 181 | constructor(message: string, requestId: string, public retryAfter: number) { |
| 182 | super(message, 429, 'rate_limit_error', requestId) |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | class AuthenticationError extends APIError { |
| 187 | constructor(message: string, requestId: string) { |
| 188 | super(message, 401, 'authentication_error', requestId) |
| 189 | } |
| 190 | } |
| 191 | ``` |
| 192 | |
| 193 | ### Error Handling in Practice |
| 194 | |
| 195 | ```typescript |
| 196 | try { |
| 197 | const user = awai |