A guide for designing and improving SDK developer experience to drive adoption and satisfaction.

Install

mkdir -p .claude/skills/sdk-dx && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16885" && unzip -o skill.zip -d .claude/skills/sdk-dx && rm skill.zip

Installs to .claude/skills/sdk-dx

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Design SDKs that developers love to use—APIs that feel native, error messages that guide, and experiences that reduce friction. This skill covers creating SDKs that drive adoption through exceptional developer experience rather than aggressive marketing. Trigger phrases: "SDK design",...
288 charsno explicit “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Design APIs that feel native to developers
  • Craft clear and guiding error messages
  • Optimize for common use cases in SDK design
  • Implement progressive disclosure for API complexity
  • Ensure sensible defaults for SDK configurations
  • Match ecosystem conventions for SDKs

How it works

The skill guides SDK design by focusing on developer experience, covering principles like API usability, clear error messages, and matching ecosystem conventions.

Inputs & outputs

You give it
SDK design requirements, API specifications, target developer audience context
You get back
SDK design principles, API structures, error message frameworks, and best practices for developer experience

When to use sdk-dx

  • Improve SDK error message clarity
  • Design intuitive API surfaces
  • Reduce developer friction in library integration

About this skill

SDK Design and Developer Experience

When to Use

Use this skill when you need design SDKs that developers love to use—APIs that feel native, error messages that guide, and experiences that reduce friction. This skill covers creating SDKs that drive adoption through exceptional developer experience rather than aggressive marketing. Trigger phrases: "SDK design",...

The best SDK marketing is an SDK that developers can't stop talking about. When your SDK makes developers feel productive and competent, they become your advocates. When it frustrates them, no amount of marketing will save you.

Overview

SDK developer experience (DX) encompasses everything a developer feels when using your library:

  • Discovery: How easily can they find and install it?
  • Learning: How quickly can they understand how to use it?
  • Using: How productive are they day-to-day?
  • Debugging: How easily can they fix problems?
  • Upgrading: How painlessly can they adopt new versions?

Great SDK DX is a competitive advantage. Developers choose tools that make them feel smart.

Before You Start

Review the developer-audience-context skill to understand:

  • What languages and frameworks do your target developers use?
  • What IDE/editor setups are most common?
  • What's their experience level with your problem domain?
  • What competing SDKs have they used? What do they like/dislike?

SDK design decisions should flow from deep understanding of your users.

API Design Principles

Principle 1: Optimize for the Common Case

The most frequent use case should require the least code.

Good Design:

# 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://..."]
)

Bad Design:

# 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)

Principle 2: Progressive Disclosure

Start simple, reveal complexity as needed.

// 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" }
});

Principle 3: Fail Fast and Clearly

Catch errors as early as possible, with actionable messages.

Good:

# 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.

Bad:

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

Principle 4: Sensible Defaults

Default values should work for most cases without configuration.

// 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

Error Messages That Guide

Error messages are documentation. Make them helpful.

The Error Message Framework

Every error message should answer:

  1. What happened?
  2. Why did it happen?
  3. How do I fix it?

Good vs. Bad Error Messages

Good:

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

Bad:

Error: 401 Unauthorized

Error Types to Distinguish

Create specific error types that developers can catch:

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

Include Context in 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"
});

Type Safety

Type safety is documentation that never goes stale.

TypeScript Best Practices

// 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 };

Autocomplete-Driven Design

Design for IDE autocomplete:

// 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? */ });

Enum and Literal Types

// 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 Integration

Make Discovery Easy

Structure your SDK so IDE features help developers:

// 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 Everywhere

/**
 * 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>

Inline Examples

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'
    """

Versioning Strategy

Semantic Versioning

Follow semver strictly:

  • MAJOR: Breaking changes (removal, signature changes)
  • MINOR: New features (backward compatible)
  • PATCH: Bug fixes (backward compatible)

What Constitutes a Breaking Change

Breaking changes (require major version bump):

  • Removing a public method or property
  • Changing method signatures
  • Changing return types
  • Changing default behavior
  • Removing support for a language/runtime version

Not breaking (minor or patch):

  • Adding new methods
  • Adding optional parameters
  • Deprecating (but not removing) features
  • Bug fixes that change incorrect behavior

Deprecation Process

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()

Migration Guides

Migration Guide Structure

# 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):

client = MyClient(api_key="...")

Why: Consistency with other SDK parameters.

2. [Next breaking change]

...

Deprecated Features Removed

  • `cli

Content truncated.

When not to use it

  • The task does not clearly match the scope of SDK design and developer experience

How it compares

This skill prioritizes developer experience and adoption through intuitive design and helpful feedback, rather than traditional marketing approaches for SDKs.

Compared to similar skills

sdk-dx side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
sdk-dx (this skill)01moReviewIntermediate
deepwiki-rs259moReviewIntermediate
codex-cli-bridge99moReviewIntermediate
skill-development179moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

deepwiki-rs

sopaco

AI-powered Rust documentation generation engine for comprehensive codebase analysis, C4 architecture diagrams, and automated technical documentation. Use when Claude needs to analyze source code, understand software architecture, generate technical specs, or create professional documentation from any programming language.

25170

codex-cli-bridge

alirezarezvani

Bridge between Claude Code and OpenAI Codex CLI - generates AGENTS.md from CLAUDE.md, provides Codex CLI execution helpers, and enables seamless interoperability between both tools

9180

skill-development

anthropics

This skill should be used when the user wants to "create a skill", "add a skill to plugin", "write a new skill", "improve skill description", "organize skill content", or needs guidance on skill structure, progressive disclosure, or skill development best practices for Claude Code plugins.

17145

skill-writer

pytorch

Guide users through creating Agent Skills for Claude Code. Use when the user wants to create, write, author, or design a new Skill, or needs help with SKILL.md files, frontmatter, or skill structure.

27126

openapi-spec-generation

wshobson

Generate and maintain OpenAPI 3.1 specifications from code, design-first specs, and validation patterns. Use when creating API documentation, generating SDKs, or ensuring API contract compliance.

22122

korean-skill-creator

clwmfksek

한글 기반 클로드 스킬 자동 생성 도구. 사용자가 "클로드 스킬을 만들어줘" 또는 "[요구사항] 스킬 만들어줘"라고 요청할 때 사용. Progressive disclosure 원칙을 따르는 한글 문서 구조(SKILL.md + references/)를 자동으로 생성하고, 실전 예시를 포함한 일관성 있는 스킬 템플릿을 제공.

8132

Search skills

Search the agent skills registry