关于 sdk-dx
面向 SDK 设计者的开发者体验技能,提供 API 设计、错误处理、类型安全和 IDE 适配方面的指导。还涵盖版本管理、迁移指南及发布前检查。
主要功能
- 01简化常见用例并逐步呈现复杂度
- 02设计清晰且可操作的错误信息
- 03应用类型安全与 IDE 自动补全
- 04遵循语义化版本并规划弃用
- 05编写迁移指南与质量检查清单
适用场景
- 01设计易上手的 SDK API
- 02改进 SDK 的错误提示
- 03规划破坏性变更与版本升级
- 04检查 SDK 发布准备情况
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ jonathimer/ devmarketing-skills --skill sdk-dx安装后在 Claude Code 中输入/sdk-dx确认已加载。
附带文件README.md
详细介绍
SDK 设计与开发者体验
最好的 SDK 营销,就是让开发者忍不住谈论你的 SDK。当你的 SDK 让开发者觉得高效又能干时,他们就会成为你的拥护者。如果它让他们感到挫败,再多的营销也无济于事。
概述
SDK 开发者体验(DX)涵盖开发者使用你的库时的所有感受:
- 发现:他们能多轻松地找到并安装它?
- 学习:他们能多快理解如何使用它?
- 使用:他们日常使用时的效率如何?
- 调试:他们能多轻松地解决问题?
- 升级:他们采用新版本时能有多省心?
出色的 SDK 开发者体验是一种竞争优势。开发者会选择让他们觉得自己很聪明的工具。
开始之前
查看 developer-audience-context 技能,了解:
- 你的目标开发者使用哪些语言和框架?
- 最常见的 IDE/编辑器配置是什么?
- 他们对你的问题领域有多少经验?
- 他们用过哪些竞品 SDK?喜欢或不喜欢哪些方面?
SDK 设计决策应基于对用户的深入了解。
API 设计原则
原则 1:针对常见场景进行优化
最常见的使用场景应只需最少的代码。
良好的设计:
# 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://..."]
)
糟糕的设计:
# 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:渐进式呈现
从简单开始,按需逐步展现复杂性。
// 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:快速且明确地失败
尽早捕获错误,并提供可执行的提示。
良好:
# 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.
糟糕:
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:合理的默认值
默认值应无需配置即可适用于大多数场景。
// 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
能引导用户的错误消息
错误消息就是文档。让它们发挥作用。
错误消息框架
每条错误消息都应回答:
- 发生了什么?
- 为什么会发生?
- 如何修复?
好与坏的错误消息
良好:
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
区分错误类型
创建特定的错误类型,供开发者捕获:
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
在错误中包含上下文
// 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 最佳实践
// 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 自动补全进行设计:
// 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? */ });
枚举和字面量类型
// 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 功能助力开发者:
// 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/文档字符串
/**
* 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: "[email protected]",
* name: "Jane Developer"
* });
*/
async createUser(input: CreateUserInput): Promise<User>
内联示例
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:
- MAJOR:不兼容的变更(移除内容、更改签名)
- MINOR:新功能(向后兼容)
- PATCH:错误修复(向后兼容)
哪些情况属于不兼容变更
不兼容变更(需要提升主版本号):
- 移除公开方法或属性
- 更改方法签名
- 更改返回类型
- 更改默认行为
- 不再支持某个语言/运行时版本
不属于不兼容变更(提升次版本号或修订号):
- 添加新方法
- 添加可选参数
- 弃用(但不移除)功能
- 修复导致不正确行为的错误
弃用流程
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()
迁移指南
迁移指南结构
# 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="...")
之后(v3):
client = MyClient(api_key="...")
原因:与其他 SDK 参数保持一致。
2. [下一个不兼容变更]
...
已移除的弃用功能
client.old_method()- 请改用client.new_method()LegacyClass- 请改用ModernClass
新功能
- [让迁移值得进行的功能]
需要帮助?
- [迁移支持渠道]
- [迁移问题答疑时段]
### Codemods and Automation
When possible, provide automated migration:
```bash
# Provide migration scripts
npx @myapi/migrate-v3
# Or codemods
npx jscodeshift -t @myapi/codemods/v2-to-v3 src/
让 SDK 符合语言习惯
语言惯用法
Python:使用 snake_case、上下文管理器和生成器
# 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:使用 Promise、async/await 和解构
// Idiomatic JS
const { data, error } = await client.users.get(id);
// Not idiomatic
client.users.get(id, function(err, result) {
if (err) { /* callback hell */ }
});
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 都有类型(语言支持时)
- 错误消息包含解决步骤
- 文档中的代码示例都会自动测试
- 更新了包含所有变更的变更日志
- 为不兼容变更提供迁移指南
- 为已移除的功能提供弃用警告
打造出色的开发者体验
- 可在 < 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 用户
评论
支持 Markdown