# Sdk Dx

> 设计开发者爱用的 SDK——原生感的 API、引导式错误消息、低摩擦体验。本技能涵盖通过卓越的开发者体验驱动采纳，而非激进营销。触发词："SDK设计"、"SDK design"、"开发者体验"、"DX设计"、"SDK开发"、"client library设计"、"SDK用户体验"、"SDK DX"……

- Skill: `kscz0000/sdk-dx` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add kscz0000/sdk-dx`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kscz0000/sdk-dx/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- License: MIT
- Author: kscz0000 (https://skillmd.com/u/kscz0000)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/kscz0000/sdk-dx

---


# SDK 设计与开发者体验
## 何时使用

当你需要设计开发者爱用的 SDK 时使用——原生感的 API、引导式错误消息、低摩擦体验。本技能涵盖通过卓越的开发者体验驱动采纳，而非激进营销。触发词："SDK设计"、"SDK design"、"开发者体验"、"DX设计"、"SDK开发"、"client library设计"、"SDK用户体验"、"SDK DX"……


最好的 SDK 营销就是让开发者赞不绝口的 SDK。当你的 SDK 让开发者感到高效和胜任时，他们会成为你的拥护者。当它让他们受挫时，再多的营销也救不了你。

## 概述

SDK 开发者体验（DX）涵盖开发者使用你的库时的一切感受：
- **发现**：他们能多容易地找到并安装它？
- **学习**：他们能多快理解如何使用它？
- **使用**：他们日常使用有多高效？
- **调试**：他们能多容易地修复问题？
- **升级**：他们能多无痛地采用新版本？

优秀的 SDK DX 是竞争优势。开发者选择让他们感觉聪明的工具。

## 开始之前

阅读 **developer-audience-context** 技能以了解：
- 你的目标开发者使用什么语言和框架？
- 最常见的 IDE/编辑器配置是什么？
- 他们在你的问题领域有多少经验？
- 他们用过哪些竞品 SDK？喜欢/不喜欢什么？

SDK 设计决策应该源于对用户的深入理解。

## API 设计原则

### 原则 1：为常见场景优化

最高频的用例应该需要最少的代码。

**好的设计：**
```python
# Common case: send a simple message
client.messages.send("Hello world", to="+1234567890")

# Full control when needed
client.messages.send(
    body="Hello world",
    to="+1234567890",
    from_="+0987654321",
    status_callback="https://...",
    media_urls=["https://..."]
)
```

**差的设计：**
```python
# Every call requires full configuration
message = Message(
    body="Hello world",
    to=PhoneNumber("+1234567890"),
    from_=PhoneNumber(config.get_default_from()),
    options=MessageOptions(
        status_callback=None,
        media_urls=[]
    )
)
client.messages.send(message)
```

### 原则 2：渐进式披露

从简单开始，按需揭示复杂性。

```javascript
// Level 1: Simplest possible usage
const result = await client.analyze("Hello world");

// Level 2: Common options
const result = await client.analyze("Hello world", {
  language: "en",
  features: ["sentiment", "entities"]
});

// Level 3: Full control
const result = await client.analyze("Hello world", {
  language: "en",
  features: ["sentiment", "entities"],
  model: "v2-large",
  timeout: 30000,
  retries: { max: 3, backoff: "exponential" }
});
```

### 原则 3：快速且清晰地失败

尽早捕获错误，提供可操作的消息。

**好的：**
```python
# Validation at construction time
client = MyClient(api_key="")
# Raises immediately: ValueError: API key cannot be empty.
# Get your API key at https://dashboard.example.com/keys

# Clear error at runtime
client.users.get("invalid-id")
# Raises: NotFoundError: User 'invalid-id' not found.
# Use client.users.list() to see available users.
```

**差的：**
```python
client = MyClient(api_key="")  # No validation
result = client.users.get("invalid-id")
# Returns: None (is this an error? empty result? who knows?)
# Or worse: raises generic Exception with stack trace
```

### 原则 4：合理默认值

默认值应该无需配置就能适用于大多数场景。

```javascript
// This should just work without configuration
const client = new MyClient({ apiKey: process.env.MY_API_KEY });

// Sensible defaults:
// - Automatic retries with exponential backoff
// - Reasonable timeouts
// - JSON content type
// - Standard auth headers
// - Connection pooling
```

## 引导式错误消息

错误消息就是文档。让它们有用。

### 错误消息框架

每条错误消息都应该回答：
1. **什么**出了问题？
2. **为什么**会出问题？
3. **如何**修复？

### 好的 vs 差的错误消息

**好的：**
```
AuthenticationError: Invalid API key provided.

The API key 'sk_test_abc...' (test key) cannot be used for
production requests.

To fix this:
1. Go to https://dashboard.example.com/keys
2. Copy your production API key (starts with 'sk_live_')
3. Update your environment variable: MY_API_KEY=sk_live_...

Docs: https://docs.example.com/authentication
```

**差的：**
```
Error: 401 Unauthorized
```

### 需要区分的错误类型

创建开发者可以捕获的特定错误类型：

```python
from myapi.errors import (
    AuthenticationError,  # Invalid/missing credentials
    AuthorizationError,   # Valid creds, insufficient permissions
    ValidationError,      # Invalid input data
    NotFoundError,        # Resource doesn't exist
    RateLimitError,       # Too many requests
    ServerError,          # Our fault, retry might help
)

try:
    client.users.get(user_id)
except NotFoundError as e:
    # Handle missing user specifically
except AuthenticationError as e:
    # Handle auth issues specifically
except MyAPIError as e:
    # Catch-all for other API errors
```

### 在错误中包含上下文

```javascript
// Bad: generic error
throw new Error("Invalid parameter");

// Good: contextual error
throw new ValidationError({
  message: "Invalid phone number format",
  field: "to",
  value: "+1abc",
  expected: "E.164 format (e.g., +14155551234)",
  docs: "https://docs.example.com/phone-numbers"
});
```

## 类型安全

类型安全是永不过时的文档。

### TypeScript 最佳实践

```typescript
// Define explicit types for all inputs and outputs
interface User {
  id: string;
  email: string;
  name: string;
  createdAt: Date;
  metadata?: Record<string, unknown>;
}

interface CreateUserInput {
  email: string;
  name: string;
  metadata?: Record<string, unknown>;
}

// Return types are explicit
async function createUser(input: CreateUserInput): Promise<User> {
  // ...
}

// Use discriminated unions for responses
type ApiResponse<T> =
  | { success: true; data: T }
  | { success: false; error: ApiError };
```

### 自动补全驱动设计

为 IDE 自动补全而设计：

```typescript
// Good: autocomplete shows all options
client.messages.create({
  to: "+1...",     // IDE shows: (property) to: string
  body: "...",    // IDE shows: (property) body: string
  // User types 'me' and sees 'mediaUrls' autocomplete
});

// Bad: requires memorization
client.send("messages", { /* what goes here? */ });
```

### 枚举与字面量类型

```typescript
// Good: constrained values with autocomplete
type MessageStatus = "queued" | "sending" | "sent" | "failed";

interface Message {
  status: MessageStatus;  // IDE shows valid values
}

// Bad: any string accepted
interface Message {
  status: string;  // No guidance, errors at runtime
}
```

## IDE 集成

### 让发现变得容易

组织 SDK 结构，让 IDE 功能帮助开发者：

```typescript
// Namespace methods logically
client.users.get(id)
client.users.list()
client.users.create(data)
client.users.update(id, data)
client.users.delete(id)

// After typing 'client.users.' the IDE shows all user operations
```

### 到处都是 JSDoc/Docstrings

```typescript
/**
 * Creates a new user in your organization.
 *
 * @param input - The user details
 * @param input.email - Must be a valid email address
 * @param input.name - Display name (max 100 characters)
 * @returns The created user with generated ID
 * @throws {ValidationError} If email format is invalid
 * @throws {ConflictError} If email already exists
 *
 * @example
 * const user = await client.users.create({
 *   email: "jane@example.com",
 *   name: "Jane Developer"
 * });
 */
async createUser(input: CreateUserInput): Promise<User>
```

### 内联示例

```python
def send_message(self, body: str, to: str, **kwargs) -> Message:
    """
    Send an SMS message.

    Args:
        body: The message content (max 1600 characters)
        to: Recipient phone number in E.164 format

    Returns:
        Message object with ID and status

    Example:
        >>> message = client.messages.send(
        ...     body="Hello from Python!",
        ...     to="+14155551234"
        ... )
        >>> print(message.status)
        'queued'
    """
```

## 版本策略

### 语义化版本

严格遵循 semver：
- **主版本**：破坏性变更（移除、签名变更）
- **次版本**：新功能（向后兼容）
- **修订版本**：Bug 修复（向后兼容）

### 什么构成破坏性变更

**破坏性变更（需要主版本号升级）：**
- 移除公共方法或属性
- 变更方法签名
- 变更返回类型
- 变更默认行为
- 移除对某个语言/运行时版本的支持

**非破坏性（次版本或修订版本）：**
- 添加新方法
- 添加可选参数
- 废弃（但不移除）功能
- 修复错误行为的 Bug

### 废弃流程

```python
import warnings

def old_method(self):
    """
    .. deprecated:: 2.3.0
       Use :meth:`new_method` instead. Will be removed in 3.0.0.
    """
    warnings.warn(
        "old_method() is deprecated, use new_method() instead. "
        "See migration guide: https://docs.example.com/migrate-v3",
        DeprecationWarning,
        stacklevel=2
    )
    return self.new_method()
```

## 迁移指南

### 迁移指南结构

```markdown
# Migrating from v2 to v3

## Overview
Version 3 introduces [major change] and removes [deprecated feature].
Migration typically takes [time estimate].

## Breaking Changes

### 1. Client Initialization
**Before (v2):**
```python
client = MyClient(key="...")
```

**After (v3):**
```python
client = MyClient(api_key="...")
```

**Why**: Consistency with other SDK parameters.

### 2. [Next breaking change]
...

## Deprecated Features Removed
- `client.old_method()` - Use `client.new_method()` instead
- `LegacyClass` - Use `ModernClass` instead

## New Features
- [Feature that makes migration worthwhile]

## Need Help?
- [Migration support channel]
- [Office hours for migration questions]
```

### Codemods 与自动化

尽可能提供自动化迁移：

```bash
# Provide migration scripts
npx @myapi/migrate-v3

# Or codemods
npx jscodeshift -t @myapi/codemods/v2-to-v3 src/
```

## 让 SDK 有原生感

### 语言惯用法

**Python**：使用 snake_case、上下文管理器、生成器
```python
# Pythonic
with client.batch() as batch:
    for user in client.users.list():
        batch.add(user.send_notification("Hello"))

# Not Pythonic
users = client.getUsers()
batch = client.createBatch()
for i in range(len(users)):
    batch.addOperation(users[i].sendNotification("Hello"))
batch.execute()
```

**JavaScript**：使用 Promises、async/await、解构
```javascript
// Idiomatic JS
const { data, error } = await client.users.get(id);

// Not idiomatic
client.users.get(id, function(err, result) {
    if (err) { /* callback hell */ }
});
```

**Go**：使用 error 返回值、接口、channel
```go
// Idiomatic Go
user, err := client.Users.Get(ctx, userID)
if err != nil {
    return fmt.Errorf("getting user: %w", err)
}

// Not idiomatic
user := client.Users.Get(userID)  // panics on error
```

### 匹配生态惯例

- 使用开发者期望的包管理器（npm、pip、gem、go get）
- 遵循该语言中流行库的命名惯例
- 集成流行框架（Express、Django、Rails）
- 支持流行的测试模式

## SDK 质量清单

### 发布前

- [ ] 所有公共 API 都有文档
- [ ] 所有公共 API 都有类型（在语言支持的情况下）
- [ ] 错误消息包含修复步骤
- [ ] 文档中的代码示例经过自动测试
- [ ] Changelog 已更新所有变更
- ] 破坏性变更有迁移指南
- [ ] 移除的功能有废弃警告

### 优秀 DX

- [ ] 快速入门在 5 分钟内成功
- [ ] IDE 自动补全适用于所有操作
- [ ] 错误可按特定类型捕获
- [ ] 重试逻辑处理瞬时故障
- [ ] 日志可配置且有用
- [ ] 调试模式显示请求/响应详情

## 工具

### SDK 生成
- **OpenAPI Generator**：从 OpenAPI 规范生成 SDK
- **Swagger Codegen**：替代生成器
- **Speakeasy**：现代 SDK 生成平台
- **Fern**：类型安全的 SDK 生成

### 测试
- **VCR/Betamax**：录制和回放 HTTP 交互
- **WireMock**：模拟 HTTP 服务
- **Pact**：契约测试

### 文档
- **TypeDoc**：TypeScript 文档
- **Sphinx**：Python 文档
- **GoDoc**：Go 文档
- **YARD**：Ruby 文档

## 相关技能

- **docs-as-marketing**：展示 SDK 能力的文档
- **api-onboarding**：SDK 的首次体验
- **changelog-updates**：有效传达 SDK 变更
- **developer-sandbox**：无需安装即可试用 SDK
- **developer-audience-context**：了解 SDK 用户

## 局限性

- 仅当任务明确匹配其上游来源和本地项目上下文时使用本技能。
- 在应用变更之前，验证命令、生成的代码、依赖项、凭证和外部服务行为。
- 不要将示例视为环境特定测试、安全审查或用户批准破坏性/高成本操作的替代。

