developer-experience
Use this skill when designing SDKs, writing onboarding flows, creating changelogs, or authoring migration guides. Triggers on developer experience (DX), API ergonomics, SDK design, getting-started guides, quickstart documentation, breaking change communication, version migration, upgrade paths, developer portals, and developer advocacy. Covers the full DX lifecycle from first impression to long-term retention.
engineering developer-experiencesdk-designonboardingchangelogmigrationdxWhat is developer-experience?
Use this skill when designing SDKs, writing onboarding flows, creating changelogs, or authoring migration guides. Triggers on developer experience (DX), API ergonomics, SDK design, getting-started guides, quickstart documentation, breaking change communication, version migration, upgrade paths, developer portals, and developer advocacy. Covers the full DX lifecycle from first impression to long-term retention.
developer-experience
developer-experience is a production-ready AI agent skill for claude-code, gemini-cli, openai-codex, and 1 more. Designing SDKs, writing onboarding flows, creating changelogs, or authoring migration guides.
Quick Facts
| Field | Value |
|---|---|
| Category | engineering |
| Version | 0.1.0 |
| Platforms | claude-code, gemini-cli, openai-codex, mcp |
| License | MIT |
How to Install
- Make sure you have Node.js installed on your machine.
- Run the following command in your terminal:
npx skills add AbsolutelySkilled/AbsolutelySkilled --skill developer-experience- The developer-experience skill is now available in your AI coding agent (Claude Code, Gemini CLI, OpenAI Codex, etc.).
Overview
Developer Experience (DX) is the practice of designing tools, SDKs, APIs, and documentation so that developers can go from zero to productive with minimal friction. Great DX reduces time-to-first-success, prevents common mistakes through ergonomic API design, and communicates changes clearly so upgrades feel safe rather than scary. This skill equips an agent to design SDKs, write onboarding flows, craft changelogs, and author migration guides that developers actually trust.
Tags
developer-experience sdk-design onboarding changelog migration dx
Platforms
- claude-code
- gemini-cli
- openai-codex
- mcp
Related Skills
Pair developer-experience with these complementary skills:
Frequently Asked Questions
What is developer-experience?
Use this skill when designing SDKs, writing onboarding flows, creating changelogs, or authoring migration guides. Triggers on developer experience (DX), API ergonomics, SDK design, getting-started guides, quickstart documentation, breaking change communication, version migration, upgrade paths, developer portals, and developer advocacy. Covers the full DX lifecycle from first impression to long-term retention.
How do I install developer-experience?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill developer-experience in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support developer-experience?
This skill works with claude-code, gemini-cli, openai-codex, mcp. Install it once and use it across any supported AI coding agent.
Maintainers
Generated from AbsolutelySkilled
SKILL.md
Developer Experience
Developer Experience (DX) is the practice of designing tools, SDKs, APIs, and documentation so that developers can go from zero to productive with minimal friction. Great DX reduces time-to-first-success, prevents common mistakes through ergonomic API design, and communicates changes clearly so upgrades feel safe rather than scary. This skill equips an agent to design SDKs, write onboarding flows, craft changelogs, and author migration guides that developers actually trust.
When to use this skill
Trigger this skill when the user:
- Wants to design or review an SDK's public API surface
- Needs to create a getting-started guide or quickstart tutorial
- Asks about developer onboarding flows or time-to-first-success
- Wants to write a changelog entry for a release
- Needs to author a migration guide for a breaking change
- Asks about API ergonomics, naming conventions, or method signatures
- Wants to evaluate or improve developer portal structure
- Needs to communicate deprecations or upgrade paths
Do NOT trigger this skill for:
- Internal team onboarding or HR processes (not developer tooling)
- End-user UX design for non-developer products (use UX skills instead)
Key principles
Time-to-first-success is the metric that matters - Every decision in DX should minimize the gap between "I found this tool" and "I got it working." If a developer cannot achieve something meaningful in under 5 minutes with your quickstart, you have a DX problem.
Pit of success over pit of despair - Design APIs so that the obvious, easy path is also the correct path. Defaults should be safe and sensible. Make it harder to misuse an API than to use it correctly.
Changelog is a contract, not a diary - Every entry should answer three questions: what changed, why it changed, and what the developer needs to do. Internal refactors that don't affect consumers do not belong in changelogs.
Migration guides are empathy documents - A breaking change without a clear migration path is a betrayal of trust. Every breaking change needs a before/after example, a mechanical upgrade path, and an explanation of why the break was necessary.
Progressive disclosure over information dump - Show developers only what they need at each stage. Quickstart shows the happy path. Guides add depth. API reference covers everything. Never front-load complexity.
Core concepts
The DX funnel mirrors a marketing funnel but for developers: Discovery (finding the tool) -> Evaluation (reading docs, trying quickstart) -> Adoption (integrating into a real project) -> Retention (staying through upgrades). Each stage has different documentation and design needs.
API surface area is the set of public methods, types, configuration options, and behaviors a developer must learn. Smaller surface area means lower cognitive load. Every public method is a commitment - it must be supported, documented, and maintained. Prefer fewer, composable primitives over many specialized methods.
The upgrade treadmill is the ongoing cost developers pay to stay current. Changelogs, deprecation notices, migration guides, and codemods are all tools to reduce this cost. High upgrade cost leads to version fragmentation and eventual abandonment.
Error messages as documentation - For many developers, the first "docs" they read are error messages. An error that says what went wrong, why, and how to fix it is worth more than a perfect API reference.
Common tasks
Design an SDK's public API
Start with use cases, not implementation. List the 5-8 most common things a developer will do, then design the minimal API that covers them.
Checklist:
- Write the README code examples first (README-driven development)
- Every method name should be a verb-noun pair (
createUser,sendEmail) - Required parameters go in the function signature; optional ones go in an options object
- Return types should be predictable - same shape for success, typed errors for failure
- Provide sensible defaults for every configuration option
- Use builder or fluent patterns only when construction is genuinely complex
// Good: minimal, predictable, composable
const client = new Acme({ apiKey: process.env.ACME_API_KEY });
const user = await client.users.create({ email: "dev@example.com" });
const invoice = await client.invoices.send({ userId: user.id, amount: 4999 });Avoid:
client.createUserAndSendWelcomeEmail()- compound operations hide behavior and make error handling ambiguous.
See references/sdk-design.md for the full SDK design checklist.
Write a getting-started guide
Structure every quickstart with this template:
- One-sentence value prop - what this tool does for the developer
- Prerequisites - language version, OS, required accounts (keep minimal)
- Install - one command, copy-pasteable
- Configure - environment variables or config file (show the minimum)
- First working example - the smallest code that produces a visible result
- Next steps - links to 2-3 natural follow-on tasks
The entire guide should be completable in under 5 minutes. If it takes longer, cut scope - move advanced setup to a separate guide.
See references/onboarding.md for the full onboarding framework.
Write a changelog entry
Follow the Keep a Changelog format. Each entry needs:
## [2.3.0] - 2026-03-14
### Added
- `client.users.archive()` method for soft-deleting users (#342)
### Changed
- `client.users.list()` now returns paginated results by default.
Pass `{ paginate: false }` to get the previous behavior.
### Deprecated
- `client.users.delete()` is deprecated in favor of `client.users.archive()`.
Will be removed in v3.0. See migration guide: /docs/migrate-v2.3
### Fixed
- `client.invoices.send()` no longer throws on zero-amount invoices (#401)Rules:
- Group by Added, Changed, Deprecated, Removed, Fixed, Security
- Link to issues/PRs with parenthetical references
- For Changed/Removed: always include the migration path inline or link to one
- Never include internal refactors that don't affect the public API
See references/changelog.md for the full changelog guide.
Author a migration guide
Every migration guide follows this structure:
- Title: "Migrating from vX to vY"
- Who needs to migrate: which users are affected
- Timeline: when the old version loses support
- Breaking changes table: change, before code, after code, reason
- Step-by-step upgrade: ordered instructions with copy-paste commands
- Codemod (if available): automated transformation script
- Verification: how to confirm the migration succeeded
- Troubleshooting: 3-5 most common migration errors and fixes
### Breaking: `createPayment` renamed to `createPaymentIntent`
**Before (v1.x):**
const payment = await client.createPayment({ amount: 1000 });
**After (v2.x):**
const intent = await client.createPaymentIntent({ amount: 1000 });
**Why:** Aligns with industry terminology. The old name implied the charge
was immediate, but the object represents an intent that may require
additional confirmation steps.
**Codemod:** `npx @acme/migrate rename-create-payment`See references/migration-guides.md for the full migration guide template.
Design error messages
Every error message should contain three parts:
- What happened - factual description of the failure
- Why it happened - the likely cause
- How to fix it - actionable next step
AcmeAuthError: Invalid API key provided.
The key starting with "sk_test_abc..." is not recognized.
Get your API key at: https://dashboard.acme.com/api-keys
Docs: https://docs.acme.com/auth#api-keysNever expose stack traces or internal identifiers in user-facing errors. Never say "something went wrong" without context.
Evaluate DX quality
Use this scorecard to audit an existing tool's developer experience:
| Dimension | Question | Score (1-5) |
|---|---|---|
| Time to first success | Can a developer get a working result in < 5 min? | |
| Error quality | Do errors explain what, why, and how to fix? | |
| API consistency | Do similar operations use similar patterns? | |
| Docs freshness | Are docs in sync with the latest release? | |
| Upgrade cost | Is there a migration guide for every breaking change? | |
| Discoverability | Can developers find what they need without support? |
A score below 3 on any dimension indicates a DX gap worth prioritizing.
Anti-patterns / common mistakes
| Mistake | Why it's wrong | What to do instead |
|---|---|---|
| Quickstart requires account creation | Adds 10+ minutes of friction before the developer sees value | Offer a sandbox mode, test keys, or local-only mode for first experience |
| Changelog says "various improvements" | Developers cannot assess upgrade risk without specifics | List every user-facing change with its impact and migration path |
| Migration guide without before/after code | Developers cannot pattern-match the change to their codebase | Always show the old code and the new code side by side |
| Exposing internal abstractions in the SDK | Coupling consumers to implementation details creates fragile upgrades | Only expose what developers need; hide internal plumbing behind a clean facade |
| Deprecating without a timeline | Developers don't know when to prioritize the migration | Always state when the deprecated feature will be removed (version or date) |
| Giant config object with no defaults | Forces developers to understand every option before they can start | Provide sensible defaults; require only what is truly required |
| Version-locked docs with no selector | Developers on older versions get wrong information | Provide a version switcher or clearly label which version each doc applies to |
Gotchas
README-driven development only works if you test the README literally - Write the quickstart, then follow it step-by-step in a fresh environment with no prior knowledge. Steps that "obviously" work to the author often fail for a developer with a different OS, different shell, or missing global dependencies.
Changelog entries for "Changed" items without migration paths create upgrade anxiety - Developers read changelogs to assess upgrade risk. A vague "Changed:
sendEmailbehavior" without before/after examples causes developers to skip the upgrade entirely rather than risk breakage.Deprecation warnings in the SDK must be impossible to miss - Emitting a
console.warnthat gets swallowed by log levels or a CI environment means developers never see the deprecation. Use both a runtime warning and a TypeScript@deprecatedJSDoc annotation so IDEs surface it at code-writing time.Error messages that expose internal stack traces in production erode security trust - Returning raw stack traces or internal service names to SDK callers is a common DX anti-pattern from copy-pasting dev-mode error handling to production. Sanitize all user-facing errors before release.
Version-locked docs that lack a version selector actively mislead developers on older versions - If your docs always show the latest API and a developer is on v1.x, they'll write broken code confidently. Either provide a version selector or prominently banner every page with the applicable version range.
References
For detailed content on specific sub-domains, read the relevant file from
references/:
references/sdk-design.md- Full SDK design checklist covering naming, error handling, configuration, and extensibility patternsreferences/onboarding.md- Complete onboarding framework with templates for quickstarts, tutorials, and developer portal structurereferences/changelog.md- Changelog format guide, semantic versioning rules, and deprecation communication playbookreferences/migration-guides.md- Migration guide template, codemod patterns, and breaking change communication strategy
Only load a references file if the current task requires deep detail on that topic.
References
changelog.md
Changelog Guide
Format: Keep a Changelog
Follow the Keep a Changelog convention. Every release gets a section with the version number, date, and categorized entries.
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- New features not yet released
## [2.3.0] - 2026-03-14
### Added
- `client.users.archive()` method for soft-deleting users (#342)
- TypeScript 5.4 support with improved type inference (#355)
### Changed
- `client.users.list()` now returns paginated results by default.
Pass `{ paginate: false }` to restore previous behavior. (#338)
### Deprecated
- `client.users.delete()` is deprecated in favor of `client.users.archive()`.
Will be removed in v3.0. See [migration guide](/docs/migrate-v2.3). (#342)
### Fixed
- `client.invoices.send()` no longer throws on zero-amount invoices (#401)
- Rate limit headers now correctly parsed on HTTP/2 connections (#398)
### Security
- Updated `xml-parser` dependency to fix CVE-2026-1234 (#410)
## [2.2.1] - 2026-02-28
...Change categories
Use exactly these categories, in this order. Omit empty categories.
| Category | When to use | Example |
|---|---|---|
| Added | New features or capabilities | New API method, new config option |
| Changed | Changes to existing functionality | Different default value, renamed param |
| Deprecated | Features marked for future removal | Method replaced by better alternative |
| Removed | Features removed in this release | Dropped support for Node 14 |
| Fixed | Bug fixes | Null pointer on empty input |
| Security | Vulnerability patches | Dependency CVE fix |
Writing good changelog entries
The three-question test
Every entry must answer:
- What changed? (the fact)
- Why does the developer care? (the impact)
- What should they do? (the action, if any)
Good vs bad entries
Bad:
- Updated user module
- Various bug fixes
- Improved performanceGood:
- `client.users.list()` now returns results sorted by `created_at` descending
by default. Pass `{ sort: "created_at:asc" }` to restore previous behavior. (#338)
- Fixed: `client.webhooks.verify()` returned false for payloads containing
unicode characters (#401)
- `client.search()` is now 3x faster for queries with > 1000 results due to
cursor-based pagination replacing offset pagination (#412)Entry format rules
- Start with the affected method, class, or feature in backticks
- Use present tense ("adds", "fixes", "removes") or past tense consistently - pick one
- Link to the issue or PR with a parenthetical reference
(#123) - For Changed and Removed: always include the migration action
- For Deprecated: always state when it will be removed (version or date)
- For Security: reference the CVE number
Semantic versioning rules
| Change type | Version bump | Example |
|---|---|---|
| Breaking change to public API | MAJOR (X.0.0) | Renamed method, removed parameter |
| New feature, backward compatible | MINOR (0.X.0) | New method, new optional parameter |
| Bug fix, backward compatible | PATCH (0.0.X) | Fixed null handling, corrected docs |
What counts as a breaking change
- Removing or renaming a public method, class, or parameter
- Changing the return type of a public method
- Changing the default value of a parameter in a way that alters behavior
- Dropping support for a runtime version (Node 14, Python 3.8, etc.)
- Changing error types that consumers might be catching
What does NOT count as breaking
- Adding a new optional parameter
- Adding a new method to an existing resource
- Fixing a bug (unless consumers depend on the buggy behavior - document this)
- Internal refactoring that doesn't change the public API
Deprecation communication playbook
Step 1: Announce deprecation (minor release)
### Deprecated
- `client.users.delete()` is deprecated. Use `client.users.archive()` instead.
`delete()` will be removed in v3.0 (estimated Q3 2026).
See [migration guide](/docs/migrate-delete-to-archive).Step 2: Runtime deprecation warning
Add a runtime warning that fires when the deprecated method is called:
/** @deprecated Use archive() instead. Will be removed in v3.0. */
async delete(id: string): Promise<void> {
console.warn(
"[acme-sdk] users.delete() is deprecated. Use users.archive() instead. " +
"See: https://docs.acme.com/migrate-delete-to-archive"
);
return this.archive(id);
}Step 3: Remove in major release
### Removed
- `client.users.delete()` has been removed. Use `client.users.archive()`.
See [v3 migration guide](/docs/migrate-v3).Deprecation timeline rules
- Minimum 1 minor release between deprecation announcement and removal
- Minimum 3 months calendar time for widely-used features
- Always provide the replacement in the deprecation notice
- Never deprecate without a migration path
Automating changelogs
From conventional commits
If using conventional commit messages (feat:, fix:, breaking:), generate
changelog entries automatically:
# Generate changelog from git history
npx conventional-changelog -p angular -i CHANGELOG.md -sFrom PR labels
Use GitHub labels (breaking, feature, fix, deprecation) and generate
changelog entries from merged PRs between releases.
Human review is still required
Automated changelogs capture the "what" but miss the "why" and "what to do." Always have a human review and enhance automated entries before publishing.
Release notes vs changelog
| Aspect | Changelog | Release notes |
|---|---|---|
| Audience | Developers integrating the SDK | Broader audience including managers |
| Tone | Technical, precise | Narrative, highlights-focused |
| Content | Every user-facing change | Top 3-5 highlights + link to full changelog |
| Location | CHANGELOG.md in repo | GitHub release page, blog post, email |
| Format | Categorized list | Prose paragraphs with screenshots/demos |
Write the changelog first, then derive release notes from it. Never the reverse.
migration-guides.md
Migration Guide Template and Patterns
Migration guide template
Use this structure for every major version migration guide.
# Migrating from v<X> to v<Y>
## Who needs to migrate
<Describe which users are affected. Be specific - "all users" is rarely true.>
- Users of `client.payments.create()` (method signature changed)
- Users with custom retry logic (retry config format changed)
- Node.js 16 users (minimum version is now Node.js 18)
## Timeline
- **v<Y> release date:** YYYY-MM-DD
- **v<X> end of support:** YYYY-MM-DD (security patches only until then)
- **v<X> end of life:** YYYY-MM-DD (no further updates)
## Quick upgrade
For most users, the upgrade is:
npm install @acme/sdk@<Y>
npx @acme/migrate v<X>-to-v<Y> # automated codemod (if available)
## Breaking changes
### 1. <Change title>
**What changed:** <factual description>
**Why:** <motivation - helps developers accept the change>
**Before (v<X>):**
<old code>
**After (v<Y>):**
<new code>
**Automated fix:** `npx @acme/migrate <specific-codemod>`
or
**Manual fix:** <step-by-step instructions>
### 2. <Next change>
...
## Non-breaking changes worth knowing
<Optional: notable improvements that don't require action but developers
should be aware of>
## Verification
After migrating, verify your integration:
npm test # run your test suite
npx @acme/sdk doctor # built-in health check (if available)
curl https://api.acme.com/v2/health # verify API connectivity
## Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| `TypeError: client.payments.create is not a function` | Using old method name | Rename to `client.paymentIntents.create()` |
| `AcmeVersionError: Unsupported API version` | SDK and API version mismatch | Set `apiVersion: "2026-01-15"` in constructor |
| ... | ... | ... |
## Getting help
- [GitHub Discussions](link) - ask the community
- [Discord #migration channel](link) - real-time help
- [Support ticket](link) - for blocking issuesBreaking change classification
Not all breaking changes are equal. Classify them to set developer expectations:
| Severity | Description | Example | Communication |
|---|---|---|---|
| High | Changes behavior silently (same code, different result) | Default sort order changed | Blog post + email + prominent banner |
| Medium | Causes compile/runtime errors (loud failure) | Method renamed, param removed | Changelog + migration guide |
| Low | Only affects edge cases or advanced usage | Custom serializer interface changed | Changelog entry with migration note |
High-severity changes need the most communication because developers may not notice the change until it causes production issues.
Codemod patterns
Codemods are automated code transformations that handle mechanical migration tasks. They dramatically reduce migration cost.
When to provide a codemod
- Method or parameter renames (mechanical find-and-replace)
- Import path changes
- Configuration format changes
- Any change affecting > 100 call sites in a typical codebase
When NOT to provide a codemod
- Logic changes that require human judgment
- Changes where the old and new patterns aren't 1:1 mappings
- Changes that affect < 5 call sites in a typical codebase
Codemod implementation options
JavaScript/TypeScript - jscodeshift:
// transforms/rename-create-payment.js
module.exports = function(fileInfo, api) {
const j = api.jscodeshift;
return j(fileInfo.source)
.find(j.MemberExpression, {
property: { name: "createPayment" }
})
.forEach(path => {
path.node.property.name = "createPaymentIntent";
})
.toSource();
};Python - libcst:
import libcst as cst
class RenameCreatePayment(cst.CSTTransformer):
def leave_Call(self, original_node, updated_node):
if m.matches(updated_node.func, m.Attribute(attr=m.Name("create_payment"))):
return updated_node.with_changes(
func=updated_node.func.with_changes(
attr=cst.Name("create_payment_intent")
)
)
return updated_nodeCLI wrapper:
# Package the codemod as a CLI tool
npx @acme/migrate rename-create-payment --dir ./src
# Output: Transformed 23 files, 47 call sites updatedCodemod testing
- Test against a fixture directory with known input/output pairs
- Include edge cases: dynamic property access, aliased imports, string references
- Run the codemod on your own SDK's test suite as a smoke test
Multi-version support strategy
When maintaining multiple major versions simultaneously:
Support matrix
| Version | Status | Support level | End of life |
|---|---|---|---|
| v3.x | Current | Full (features + fixes + security) | - |
| v2.x | Maintenance | Fixes + security only | 2026-12-31 |
| v1.x | End of life | No support | 2025-06-30 |
Branch strategy
main -> v3.x (current)
release/v2 -> v2.x (maintenance - cherry-pick critical fixes)
release/v1 -> v1.x (archived, no changes)Documentation strategy
- Default docs show the current version
- Provide a version switcher for at least current and previous major
- Archive older versions at a stable URL (e.g.,
/docs/v1/) - Never delete old docs - developers on legacy versions need them
Communication channels for breaking changes
Use multiple channels based on change severity:
| Channel | When | Audience |
|---|---|---|
| Changelog entry | Every breaking change | Developers reading release notes |
| Migration guide | Every major version | Developers upgrading |
| Blog post | High-severity changes | Broader developer community |
| Email to registered devs | High-severity changes | Active users |
| In-SDK deprecation warning | One version before removal | Developers running the code |
| Dashboard banner | High-severity API changes | Developers with active integrations |
| Status page notice | API version sunset | All API consumers |
Communication timeline
- T-6 months: Announce upcoming major version in blog + email
- T-3 months: Release beta/RC with migration guide
- T-0: Release major version, begin previous version maintenance period
- T+6 months: Send reminder emails about previous version EOL
- T+12 months: End of life for previous version
Migration checklist for SDK authors
Before releasing a major version:
- Every breaking change has a before/after code example
- Migration guide is written and reviewed by someone who didn't make the changes
- Codemods exist for all mechanical changes
- Codemods are tested against real-world codebases (not just unit tests)
- Previous version has a documented end-of-support date
- Previous version docs are archived at a stable URL
- Deprecation warnings were shipped at least one minor version ago
- Changelog is complete with all breaking changes categorized
- Blog post / email / announcement is drafted
- Support team is briefed on common migration questions
onboarding.md
Developer Onboarding Framework
The five-minute rule
A developer should go from "I have never used this tool" to "I got a working result" in under five minutes. This is the single most important DX metric.
To achieve this:
- Installation must be a single command
- Authentication must be copy-paste (test key, sandbox, or local mode)
- The first example must produce a visible, meaningful result
- No account creation should be required for the first experience
Quickstart template
Every quickstart follows this structure. Do not deviate.
# Quickstart
<Tool Name> lets you <one-sentence value prop>.
## Prerequisites
- <Language> >= <version>
- A <Tool Name> account ([sign up free](link)) -- only if truly required
## Install
<single install command>
## Set your API key
export TOOL_API_KEY=your-test-key-here
Get a test key at: <link to dashboard>
## Send your first <thing>
<5-15 lines of code that produce a visible result>
Run it:
<command to execute>
You should see:
<expected output>
## Next steps
- [Send a <thing> with custom options](/docs/guides/custom-options)
- [Handle errors and retries](/docs/guides/error-handling)
- [Go to production](/docs/guides/production-checklist)Quickstart anti-patterns
| Pattern | Problem | Fix |
|---|---|---|
| Requires Docker/infra setup | Adds 10+ min before the first line of code | Offer a hosted sandbox or mock mode |
| Shows 50+ lines of config | Overwhelms the developer before they see value | Show minimum config; link to full options |
| First example requires domain knowledge | Developer can't follow along without context | Use a universal example (hello world, send email) |
| No expected output shown | Developer can't verify success | Always show what success looks like |
| Links to "full documentation" without specifics | Developer gets lost in a docs maze | Link to 2-3 specific next-step guides |
Tutorial structure
Tutorials go deeper than quickstarts. They teach one complete workflow.
Template
# <Verb> + <Noun> (e.g. "Send a transactional email")
In this tutorial you will:
1. <Step 1 outcome>
2. <Step 2 outcome>
3. <Step 3 outcome>
**Time:** ~10 minutes
**Prerequisites:** Completed the [Quickstart](/docs/quickstart)
## Step 1: <Action>
<1-2 sentences of context>
<code block>
<Explain what happened and why>
## Step 2: <Action>
...
## What you built
<Summary of what the developer accomplished>
## Next steps
- <Related tutorial 1>
- <Related tutorial 2>Tutorial principles
- One tutorial = one workflow (don't combine "send email" and "set up webhooks")
- Every step must produce a verifiable intermediate result
- Show the code first, explain after
- Time estimates must be honest - test them with a fresh developer
Developer portal structure
Information architecture
Organize docs by developer intent, not by product structure:
/docs
/quickstart - First 5 minutes
/guides - Task-oriented walkthroughs
/authentication
/sending-emails
/handling-webhooks
/going-to-production
/api-reference - Every method, parameter, type
/changelog - What changed and when
/migration - Upgrade guides for breaking changes
/sdks - Language-specific setup and quirks
/examples - Copy-paste recipes for common patternsNavigation principles
- Use task-oriented labels ("Send an email") not feature-oriented ("Email API")
- Put quickstart in the top-level navigation, never buried in a submenu
- Provide a search that covers docs, API reference, and changelog
- Show the most recent SDK version prominently with a version switcher
Onboarding email sequence
For tools with accounts, design the onboarding email sequence around DX milestones:
| Timing | Content | |
|---|---|---|
| Welcome | Immediate | Link to quickstart, test API key, one-click sandbox |
| First success check | Day 1 | "Did you send your first X? Here's how" + quickstart link |
| Integration guide | Day 3 | "Ready to go deeper?" + top 3 guides for their use case |
| Production checklist | Day 7 | Security best practices, rate limits, monitoring setup |
| Community invite | Day 14 | Discord/Slack/forum link, office hours schedule |
Segmentation
- Detect the developer's language/framework from their first API call
- Tailor follow-up emails with language-specific examples
- Track activation metrics: installed SDK, made first API call, integrated in app
Measuring onboarding success
| Metric | Target | How to measure |
|---|---|---|
| Time to first API call | < 5 min | Timestamp between account creation and first request |
| Quickstart completion rate | > 60% | Track page scroll or step completion events |
| Day-7 retention | > 40% | Developer made API calls on 2+ distinct days in first week |
| Support ticket rate (first week) | < 10% | Tickets filed / new signups |
| Activation rate | > 30% | Developers who reach a defined "aha moment" within 14 days |
Common onboarding pitfalls
- Requiring production credentials for sandbox - Always offer test/sandbox keys that work immediately without approval workflows
- Docs lag behind SDK releases - Automate doc generation from code; never let docs be more than one release behind
- No copy button on code blocks - Small friction multiplied by every developer on every page
- Assuming framework knowledge - Not every Node.js developer uses Express; show framework-agnostic examples first
- Hiding the pricing page - Developers evaluate cost early; making them hunt for pricing destroys trust
sdk-design.md
SDK Design Checklist
Naming conventions
Methods
- Use verb-noun pairs:
createUser,listInvoices,deleteWebhook - Use consistent verbs across resources:
create,get,list,update,delete - Avoid ambiguous verbs:
handle,process,manage,do - Boolean methods start with
is,has,can,should - Avoid negated names:
isEnablednotisNotDisabled
Resources
- Use the same noun the API uses (don't rename
payment_intenttocharge) - Plural for collections:
client.users.list()notclient.user.list() - Singular for singletons:
client.account.get()notclient.accounts.get()
Parameters
- Required params go in the function signature positionally
- Optional params go in a trailing options/config object
- Never use positional booleans:
send(email, true)- what doestruemean?
// Bad: positional boolean
await client.emails.send("user@example.com", true, false);
// Good: options object
await client.emails.send("user@example.com", {
trackOpens: true,
trackClicks: false,
});Constructor and configuration
Minimum viable constructor
The constructor should require only what is truly necessary to authenticate. Everything else gets a sensible default.
// Minimum: just the API key
const client = new Acme({ apiKey: "sk_..." });
// Optional overrides for advanced use cases
const client = new Acme({
apiKey: "sk_...",
baseUrl: "https://api.staging.acme.com",
timeout: 30_000,
retries: 3,
logger: customLogger,
});Environment variable fallback
SDKs should check for well-known environment variables when no explicit config is provided. Document which env vars are checked.
// Constructor checks process.env.ACME_API_KEY if apiKey is omitted
const client = new Acme(); // works if ACME_API_KEY is setReturn types and error handling
Consistent return shapes
Every method should return the same shape for success. Use typed errors
for failure - never return null to indicate an error.
// Good: consistent returns
const user = await client.users.get("usr_123"); // returns User
const users = await client.users.list(); // returns User[]
// Good: typed errors
try {
await client.users.get("usr_999");
} catch (error) {
if (error instanceof AcmeNotFoundError) {
// handle 404
}
}Error hierarchy
Design a small, useful error hierarchy:
AcmeError (base)
AcmeApiError (any API response error)
AcmeNotFoundError (404)
AcmeValidationError (400/422)
AcmeAuthError (401/403)
AcmeRateLimitError (429)
AcmeConnectionError (network failures)
AcmeTimeoutError (request timeout)Every error should include:
- Human-readable message with fix suggestion
- HTTP status code (if from API)
- Request ID for support escalation
- Original response body for debugging
Pagination
Never return unbounded lists. Default to paginated responses.
// Cursor-based pagination (preferred)
const page1 = await client.users.list({ limit: 20 });
const page2 = await client.users.list({ limit: 20, after: page1.cursor });
// Convenience: auto-pagination iterator
for await (const user of client.users.list({ limit: 20 })) {
console.log(user.email);
}Idempotency
For any operation that creates or mutates state, support idempotency keys:
await client.payments.create(
{ amount: 1000, currency: "usd" },
{ idempotencyKey: "order_abc_payment_1" }
);Document clearly which methods are idempotent by default and which require an explicit key.
Extensibility
Middleware / hooks
Allow consumers to intercept requests for logging, metrics, or custom headers:
const client = new Acme({
apiKey: "sk_...",
beforeRequest: (req) => {
req.headers["X-Request-Source"] = "my-service";
return req;
},
afterResponse: (res) => {
metrics.record("acme_api_call", { status: res.status });
return res;
},
});Custom HTTP client
Allow injecting a custom HTTP client for environments with specific networking requirements (proxies, custom TLS, etc.).
Versioning
- Follow semantic versioning strictly
- Never ship breaking changes in minor or patch releases
- Use header-based API versioning (
Acme-Version: 2024-01-15) over URL versioning - Document the SDK-to-API version mapping clearly
- Support at least the current and previous major API version simultaneously
Testing support
Make the SDK testable without hitting the real API:
// Provide a mock/test mode
const client = new Acme({ apiKey: "sk_test_..." }); // test mode by key prefix
// Or provide explicit test helpers
import { createMockClient } from "@acme/sdk/testing";
const mock = createMockClient();
mock.users.get.mockResolvedValue({ id: "usr_123", email: "test@example.com" });Documentation requirements for SDKs
Every SDK must ship with:
- README with install + quickstart (< 30 lines of code to first result)
- API reference generated from types/docstrings (not hand-written)
- At least 3 guides covering the most common workflows
- Changelog following Keep a Changelog format
- Migration guide for every major version bump
Frequently Asked Questions
What is developer-experience?
Use this skill when designing SDKs, writing onboarding flows, creating changelogs, or authoring migration guides. Triggers on developer experience (DX), API ergonomics, SDK design, getting-started guides, quickstart documentation, breaking change communication, version migration, upgrade paths, developer portals, and developer advocacy. Covers the full DX lifecycle from first impression to long-term retention.
How do I install developer-experience?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill developer-experience in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support developer-experience?
developer-experience works with claude-code, gemini-cli, openai-codex, mcp. Install it once and use it across any supported AI coding agent.