| name | docs-kit |
| description | Unified documentation toolkit — generate structured technical docs (API, architecture, guides, READMEs), discover and synthesize docs across local files and external sources, write conventional changelogs from git history, explain code at configurable depths, produce OpenAPI specs with code generation, design Swagger/Redoc-style API reference. Single entry point for any docs task. |
| layer | utility |
| category | documentation |
| triggers | ["API documentation","ELI5 this code","JSDoc","README","api docs","api documentation","api reference","api spec","architecture doc","code walkthrough","deep dive","docstring","document this","explain like I'm","explain this code","find documentation","find examples for","generate changelog","how does this library work","how does this work","interactive api docs","look up the docs","openapi","openapi spec","redoc","reference for","release notes","runbook","swagger","swagger ui","update CHANGELOG","walk me through","what changed","what does this API do","what does this do","write a guide","write changelog","write docs"] |
docs-kit
Unified documentation toolkit — generate structured technical docs (API, architecture, guides, READMEs), discover and synthesize docs across local files and external sources, write conventional changelogs from git history, explain code at configurable depths, produce OpenAPI specs with code generation, design Swagger/Redoc-style API reference. Single entry point for any docs task.
Absorbs
docs-writer
docs-seeker
changelog-writer
code-explainer
api-documentation
openapi
From docs-writer
Generate clear, structured technical documentation — API docs, architecture docs, guides, runbooks, and inline code documentation
Docs Writer Skill
Purpose
Write documentation that developers actually read. This skill generates documentation at the right level of detail for the target audience, follows consistent structure, and stays maintainable by staying close to the code it describes.
Key Concepts
Documentation Types
| Type | Audience | Purpose | Format |
|---|
| API Reference | Developers integrating | Exhaustive endpoint/function docs | OpenAPI, JSDoc, markdown |
| Architecture Doc | Team, new hires | System design and decisions | Markdown + diagrams |
| Tutorial | New users | Learn by doing | Step-by-step markdown |
| How-To Guide | Experienced users | Solve specific problems | Task-oriented markdown |
| Runbook | Ops / on-call | Incident response procedures | Checklist markdown |
| ADR | Team, future maintainers | Record architecture decisions | Markdown template |
| README | Everyone | First impression, quick start | Markdown |
| Inline Docs | Code readers | Explain why, not what | JSDoc, docstrings, comments |
The Four Quadrants (Diataxis Framework)
LEARNING WORKING
┌─────────────────┬─────────────────┐
PRACTICAL │ Tutorials │ How-To Guides │
│ (learning- │ (problem- │
│ oriented) │ oriented) │
├─────────────────┼─────────────────┤
THEORY │ Explanation │ Reference │
│ (understanding-│ (information- │
│ oriented) │ oriented) │
└─────────────────┴─────────────────┘
Each quadrant serves a different need. Do not mix them in a single document.
Workflow
Step 1: Identify Audience and Type
Ask:
- Who will read this? (junior dev, senior dev, ops, end user)
- When will they read it? (onboarding, debugging, integrating, incident)
- What do they need to accomplish? (understand, implement, fix, decide)
Step 2: Choose Structure
API Reference Structure
# API Reference: [Service Name]
## Base URL
`https://api.example.com/v1`
## Authentication
All requests require a Bearer token in the Authorization header.
## Endpoints
### Create User
`POST /users`
Creates a new user account.
**Request Body:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `email` | string | Yes | Valid email address |
| `name` | string | Yes | Display name (1-100 chars) |
| `role` | string | No | One of: `user`, `admin`. Default: `user` |
**Example Request:**
```json
{
"email": "alice@example.com",
"name": "Alice Chen",
"role": "admin"
}
Response: 201 Created
{
"id": "usr_abc123",
"email": "alice@example.com",
"name": "Alice Chen",
"role": "admin",
"createdAt": "2025-06-15T10:30:00Z"
}
Error Responses:
| Status | Code | Description |
|---|
| 400 | INVALID_EMAIL | Email format is invalid |
| 409 | EMAIL_EXISTS | Email already registered |
| 422 | VALIDATION_ERROR | Request body validation failed |
#### Architecture Document Structure
```markdown
# [System Name] Architecture
## Overview
One paragraph explaining what this system does and why it exists.
## Context Diagram
[Mermaid C4 diagram showing system in its environment]
## Key Design Decisions
1. **Decision**: Why we chose X over Y
- Context: What problem we faced
- Decision: What we chose
- Consequences: Trade-offs accepted
## Components
### [Component A]
- **Responsibility**: What it does
- **Technology**: What it uses
- **Interfaces**: How other components interact with it
## Data Flow
[Mermaid sequence diagram for critical flows]
## Non-Functional Requirements
- Performance targets
- Availability requirements
- Security constraints
## Deployment
How and where this runs in production.
ADR (Architecture Decision Record)
# ADR-NNN: [Title of Decision]
## Status
[Proposed | Accepted | Deprecated | Superseded by ADR-XXX]
## Context
What is the issue that we are seeing that motivates this decision?
## Decision
What is the change that we are proposing and/or doing?
## Consequences
What becomes easier or harder because of this change?
### Positive
- Benefit 1
- Benefit 2
### Negative
- Trade-off 1
- Trade-off 2
### Risks
- Risk 1 and mitigation
Runbook Structure
# Runbook: [Incident Type]
## Severity
P1 / P2 / P3
## Symptoms
- What alerts fire
- What users report
- What metrics look abnormal
## Diagnosis Steps
1. Check [specific dashboard/metric]
2. Run `command to check logs`
3. Verify [specific system state]
## Resolution Steps
1. [ ] Step 1 with exact command
2. [ ] Step 2 with exact command
3. [ ] Verify fix: [how to confirm]
## Escalation
If unresolved after 15 minutes:
- Contact: [team/person]
- Slack: [channel]
## Post-Incident
- [ ] Write incident report
- [ ] Update this runbook if procedures changed
- [ ] Create tickets for preventive measures
Step 3: Write Inline Documentation
JSDoc / TypeScript
function calculateCompoundInterest(
principal: number,
annualRate: number,
compoundsPerYear: number,
years: number
): number {
if (principal < 0) throw new RangeError('Principal cannot be negative');
if (annualRate < 0) throw new RangeError('Annual rate cannot be negative');
return Math.round(
principal * Math.( + annualRate / compoundsPerYear, compoundsPerYear * years) *
) / ;
}
Python Docstrings
def calculate_compound_interest(
principal: float,
annual_rate: float,
compounds_per_year: int,
years: int,
) -> float:
"""Calculate compound interest on a principal amount.
Uses the formula: A = P(1 + r/n)^(nt)
Args:
principal: Initial investment amount in dollars. Must be non-negative.
annual_rate: Annual interest rate as decimal (e.g., 0.05 for 5%).
compounds_per_year: Times interest compounds per year (e.g., 12 for monthly).
years: Duration in years.
Returns:
Total amount after compound interest, rounded to 2 decimal places.
Raises:
ValueError: If principal or annual_rate is negative.
Example:
>>> calculate_compound_interest(1000, 0.05, 12, 10)
1647.01
"""
Step 4: Comments — When and How
Comment the WHY, not the WHAT:
counter++;
if (user.role === 'admin') { ... }
const MAX_RETRIES = 3;
items.sort((a, b) => b.createdAt - a.createdAt);
const expiry = decodeJwtExpiry(token);
Documentation Quality Checklist
Auto-Generation Tips
- Use TypeScript types to generate API docs (TypeDoc, tsdoc)
- Use OpenAPI spec to generate REST API docs (Swagger UI, Redoc)
- Use Storybook for component documentation
- Use
jest --coverage output to identify undocumented code
- Prefer co-located docs (JSDoc in code) over separate files — they stay in sync better
From docs-seeker
Documentation discovery, retrieval, and synthesis across local files, APIs, and external sources
Docs Seeker
Purpose
This skill systematically locates, retrieves, and synthesizes documentation from multiple sources. It prevents hallucination by grounding responses in actual documentation rather than training data, and it ensures version-accurate information by checking the correct version of library docs.
Key Concepts
Documentation Source Hierarchy
Always prefer higher-reliability sources:
| Priority | Source | Reliability | Tool |
|---|
| 1 | Official docs (current version) | Highest | Context7, WebFetch |
| 2 | Source code / type definitions | High | Read, Grep, Glob |
| 3 | Official examples / tutorials | High | Context7, WebFetch |
| 4 | README / CHANGELOG in repo | High | Read |
| 5 | GitHub Issues / Discussions | Medium | WebSearch, gh CLI |
| 6 | Stack Overflow (high-vote) | Medium | WebSearch |
| 7 | Blog posts / tutorials | Low-Medium | WebSearch |
| 8 | Training data (model memory) | Lowest | Internal (last resort) |
Version Awareness
Documentation is only useful if it matches the version in use:
STEP 1: Determine the version in use
- Check package.json / requirements.txt / go.mod / Cargo.toml
- Check lock files for exact resolved version
- Check runtime: node -e "require('pkg/package.json').version"
STEP 2: Retrieve docs for THAT version
- Context7: Specify version if available
- Official docs: Use versioned URL (e.g., docs.example.com/v3/)
- GitHub: Use tagged release (e.g., github.com/org/repo/tree/v3.2.1)
STEP 3: Flag version mismatches
- If docs are for a different version, explicitly note the discrepancy
- Highlight breaking changes between versions
Workflow
Phase 1: Query Analysis
Before searching, understand what is actually needed:
QUERY: "How do I use middleware in Express?"
ANALYSIS:
- Library: Express.js
- Topic: Middleware registration and execution
- Scope: Usage patterns (not internal implementation)
- Version needed: Check project's package.json
- Depth: Practical examples, not theoretical overview
Phase 2: Local Documentation Search
Check the project itself first:
SEARCH ORDER:
1. README.md, CONTRIBUTING.md in project root
2. /docs directory (if exists)
3. Inline code comments and JSDoc/docstrings
4. Type definitions (.d.ts, type hints)
5. Test files (often serve as usage documentation)
6. Configuration files (reveal available options)
7. CHANGELOG.md (version-specific behavior changes)
Tools to use:
Glob to find documentation files: **/*.md, **/docs/**
Grep to search for specific terms across the codebase
Read to examine specific files
Phase 3: External Documentation Retrieval
STRATEGY A — Context7 (preferred for libraries):
1. Resolve library ID: context7.resolve-library-id
2. Query specific topic: context7.query-docs
3. Extract code examples and API signatures
STRATEGY B — Official Documentation Sites:
1. WebFetch the documentation URL
2. Process with specific extraction prompt
3. Verify version matches
STRATEGY C — Web Search (for niche topics):
1. WebSearch with specific query including version
2. Filter results by source reliability
3. Cross-reference multiple sources
STRATEGY D — Source Code (when docs are insufficient):
1. Find the library in node_modules / site-packages / vendor
2. Read the source code directly
3. Extract behavior from implementation
Phase 4: Synthesis
Combine findings into actionable documentation:
TOPIC: [What was asked about]
VERSION: [Library/tool version in use]
SUMMARY: [1-3 sentence overview]
API REFERENCE:
[Function/method signatures with parameter descriptions]
USAGE PATTERN:
[Code example showing the standard usage]
COMMON PITFALLS:
[Known gotchas, edge cases, or mistakes]
RELATED:
[Links to related APIs or concepts]
SOURCES:
- [Source 1 — reliability rating]
- [Source 2 — reliability rating]
Search Patterns
Pattern: API Signature Discovery
When you need to know what a function accepts and returns:
1. Check TypeScript definitions:
Glob: node_modules/@types/{package}/**/*.d.ts
Glob: node_modules/{package}/dist/**/*.d.ts
2. Check source type annotations:
Grep: "export function {functionName}" in node_modules/{package}
3. Check official docs:
Context7: query for "{functionName} parameters return type"
4. Check tests for usage examples:
Grep: "{functionName}" in node_modules/{package}/**/*.test.*
Pattern: Configuration Options
When you need to know all available configuration options:
1. Check TypeScript config types:
Grep: "interface.*Config" or "type.*Options" in package source
2. Check default configuration:
Grep: "defaultConfig" or "defaults" in package source
3. Check JSON schemas:
Glob: node_modules/{package}/**/*.schema.json
4. Check documentation:
Context7: query for "configuration options"
Pattern: Migration Guide
When upgrading a dependency:
1. Check CHANGELOG:
Read: node_modules/{package}/CHANGELOG.md
2. Check migration docs:
WebSearch: "{package} migration guide v{old} to v{new}"
3. Check breaking changes:
WebSearch: "{package} v{new} breaking changes"
GitHub: gh release view v{new} --repo {org}/{repo}
4. Check codemods:
WebSearch: "{package} codemod v{new}"
Pattern: Error Message Resolution
When encountering an unknown error from a library:
1. Search error message in source:
Grep: "exact error message text" in node_modules/{package}
2. Find the throwing code to understand the cause:
Read the file found above, examine the condition that triggers the error
3. Search GitHub issues:
WebSearch: "{package} {error message}" site:github.com
4. Search community:
WebSearch: "{package} {error message}"
Documentation Quality Assessment
Rate retrieved documentation before presenting it:
QUALITY CRITERIA:
✓ Version-matched: Docs match the version in use
✓ Complete: Covers the specific question asked
✓ Accurate: Verified against source code or multiple sources
✓ Current: Not deprecated or outdated
✓ Actionable: Includes usable code examples
CONFIDENCE LEVELS:
HIGH: Official docs, version-matched, with code examples
MEDIUM: Official docs but different version, or community source with high engagement
LOW: Single blog post, old Stack Overflow answer, or training data only
UNCERTAIN: Conflicting sources or no documentation found
Usage Examples
Example: Finding React Server Component Docs
QUERY: "How to use async server components in Next.js App Router?"
STEP 1: Check project version
→ package.json shows next@14.2.0
STEP 2: Context7
→ Resolve: next.js → /vercel/next.js
→ Query: "async server components app router"
→ Result: Documentation with code examples
STEP 3: Synthesize
TOPIC: Async Server Components (Next.js 14 App Router)
VERSION: Next.js 14.2.0 ✓ (supported since 13.4)
SUMMARY: Server Components in App Router are async by default.
You can use await directly in the component body.
USAGE:
export default async function Page() {
const data = await fetchData();
return <div>{data.title}</div>;
}
PITFALLS:
- Cannot use hooks (useState, useEffect) in server components
- Cannot pass functions as props to client components
- Use 'use client' directive for interactive components
SOURCES:
- Context7: /vercel/next.js (HIGH reliability)
Anti-Patterns
- Trusting training data over docs: Always verify with actual documentation. Training data may be outdated or contain errors from unreliable sources.
- Version-blind searching: Retrieving docs without checking the project's actual version leads to incorrect API usage.
- Single source reliance: Cross-reference at least two sources for critical information, especially for complex configurations.
- Ignoring local docs: The project's own documentation, tests, and type definitions are often the most accurate and relevant source.
- Over-fetching: Retrieving entire documentation pages when only a specific section is needed wastes context budget.
Integration Notes
- Always check version before querying external sources — use
Read on package manifests first.
- Feed results through context-engineering to compress documentation for context-constrained situations.
- When documentation reveals a complex setup, hand off to sequential-thinking for step-by-step implementation.
- Use repomix when the documentation question requires understanding the full repository structure.
From changelog-writer
Generate structured changelogs from git commits, PRs, and tags following Keep a Changelog conventions
Changelog Writer Skill
Purpose
Generate human-readable changelogs that communicate what changed, why it matters, and whether users need to take action. A good changelog bridges the gap between git history (for developers) and release notes (for users).
Key Concepts
Keep a Changelog Format (Recommended)
The standard format from keepachangelog.com:
# 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 feature description
### Changed
- Existing feature modification
### Deprecated
- Feature that will be removed in future versions
### Removed
- Feature that was removed
### Fixed
- Bug fix description
### Security
- Vulnerability fix description
## [1.2.0] - 2025-06-15
### Added
- User export functionality (#123)
- Dark mode support for dashboard (#145)
### Fixed
- Login timeout on slow connections (#156)
- Incorrect date formatting in UTC-negative timezones (#160)
## [1.1.0] - 2025-05-01
...
[Unreleased]: https://github.com/user/repo/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/user/repo/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/user/repo/compare/v1.0.0...v1.1.0
Change Categories
| Category | When to Use | Triggers Version Bump |
|---|
| Added | New features | Minor |
| Changed | Modifications to existing features | Minor (or Major if breaking) |
| Deprecated | Features that will be removed | Minor |
| Removed | Features that were removed | Major |
| Fixed | Bug fixes | Patch |
| Security | Vulnerability patches | Patch (or Minor/Major) |
Semantic Versioning Mapping
MAJOR (X.0.0) — Breaking changes
- Removed features
- Changed API contracts
- Renamed public interfaces
- Changed default behavior
MINOR (0.X.0) — New features, non-breaking
- Added features
- Added endpoints/parameters
- Deprecated features (still work)
PATCH (0.0.X) — Bug fixes, non-breaking
- Fixed bugs
- Security patches
- Performance improvements (no API change)
Workflow
Step 1: Collect Raw Changes
git log $(git describe --tags --abbrev=0)..HEAD --oneline --no-merges
git log v1.1.0..v1.2.0 --oneline --no-merges
git log v1.1.0..HEAD --format="%s|%h|%an" --no-merges
gh pr list --state merged --base main --search "merged:>2025-05-01" --json number,title,labels,author
Step 2: Categorize Changes
Map conventional commit types to changelog categories:
Commit Type → Changelog Category
feat: → Added
fix: → Fixed
docs: → (usually omit from changelog)
style: → (omit)
refactor: → Changed (only if user-facing behavior changes)
perf: → Changed (or Fixed, depending on context)
test: → (omit)
chore: → (omit)
ci: → (omit)
build: → (omit)
revert: → Removed or Fixed (depending on what was reverted)
BREAKING CHANGE: → always include, note prominently
security: → Security
deprecate: → Deprecated
Step 3: Write Human-Readable Entries
Transformation rules:
Git commit (developer language):
"fix(auth): handle race condition in token refresh when multiple tabs open simultaneously"
Changelog entry (user language):
"Fixed login session being lost when using the app in multiple browser tabs"
---
Git commit:
"feat(api): add GET /users/:id/export endpoint with CSV and JSON format support"
Changelog entry:
"Added user data export in CSV and JSON formats via the API"
---
Git commit:
"refactor(db): migrate from raw SQL to Prisma ORM"
Changelog entry:
(omit — internal change, no user-facing impact)
---
Git commit:
"feat(auth)!: require email verification for new accounts"
Changelog entry (Breaking):
"**BREAKING**: New user accounts now require email verification before first login.
Existing accounts are not affected."
Step 4: Determine Version Bump
Analyze all changes since last release:
Has any commit:
- Removed a public API/feature? → MAJOR
- Changed behavior in a breaking way? → MAJOR
- Has `BREAKING CHANGE:` footer? → MAJOR
- Has `!` after type (e.g., `feat!:`)? → MAJOR
If no breaking changes, has any commit:
- Added a new feature (`feat:`)? → MINOR
- Added new API endpoint/parameter? → MINOR
- Deprecated a feature? → MINOR
If only fixes:
- Bug fixes (`fix:`)? → PATCH
- Security patches? → PATCH
- Performance improvements? → PATCH
- Documentation updates? → PATCH (or no bump)
Step 5: Format and Write
cat <<'ENTRY'
- User data export in CSV and JSON formats ([#178](link))
- Dark mode toggle in user preferences ([#182](link))
- Keyboard shortcuts for common actions ([#185](link))
- Improved search performance for large datasets — results now load 3x faster ([#180](link))
- Updated password requirements to minimum 12 characters ([#183](link))
- Session loss when switching between browser tabs ([#176](link))
- Incorrect timezone display for users in UTC-negative zones ([#179](link))
- PDF export failing for reports with special characters in titles ([#181](link))
- Patched XSS vulnerability in comment rendering ([#184](link))
ENTRY
Audience-Specific Formats
Developer Changelog (Technical)
## [1.3.0] - 2025-06-20
### Added
- `GET /api/users/:id/export` endpoint with `format` query param (csv|json) (#178)
- `prefers-color-scheme` media query support in theme system (#182)
### Changed
- **BREAKING**: `SearchService.query()` now returns `Promise<PaginatedResult>` instead of `Promise<Result[]>` (#180)
- Minimum password length increased from 8 to 12 in `PasswordValidator` (#183)
### Fixed
- Race condition in `TokenRefreshService` when multiple instances race (#176)
- `DateFormatter.toLocal()` returns wrong offset for negative UTC zones (#179)
End-User Release Notes (Non-Technical)
## What's New in Version 1.3
### New Features
- **Export your data** — Download your account data as a spreadsheet (CSV) or data file (JSON) from Settings > Privacy.
- **Dark mode** — Switch to dark mode in Settings > Appearance, or let it follow your system preference.
- **Keyboard shortcuts** — Press `?` anywhere to see available shortcuts.
### Improvements
- **Faster search** — Search results now load up to 3x faster for large workspaces.
### Bug Fixes
- Fixed an issue where you could be logged out when switching browser tabs.
- Fixed timezone display being incorrect for some regions.
- Fixed PDF exports failing when report titles contained special characters.
### Important
- Passwords now require at least 12 characters. Existing passwords are not affected until your next password change.
Automation Integration
generate_changelog() {
local from_tag="$1"
local to_ref="${2:-HEAD}"
echo "## Changes since ${from_tag}"
echo ""
local added=$(git log "${from_tag}..${to_ref}" --oneline --no-merges --grep="^feat" --format="- %s (%h)")
if [[ -n "$added" ]]; then
echo "### Added"
echo "$added" | sed 's/^- feat[^:]*: /- /'
echo ""
fi
local fixed=$(git log "${from_tag}..${to_ref}" --oneline --no-merges --grep="^fix" --format="- %s (%h)")
if [[ -n "$fixed" ]]; then
echo "### Fixed"
echo "$fixed" | sed 's/^- fix[^:]*: /- /'
breaking=$(git --oneline --no-merges --grep= --format=)
[[ -n ]];
}
Quality Checklist
From code-explainer
Explain code at configurable depth levels — from high-level overview to line-by-line analysis with algorithm complexity and design pattern identification
Code Explainer Skill
Purpose
Make code understandable to the reader at their level. This skill explains code at configurable depths — from a one-sentence summary to a line-by-line breakdown — while identifying patterns, complexity, and potential issues. The goal is not just to describe what code does, but to build understanding of why it works.
Key Concepts
Depth Levels
| Level | When to Use | Output |
|---|
| L1: Overview | Quick understanding, PR review | 1-3 sentence summary of purpose |
| L2: Standard | Code review, onboarding | Purpose, flow, key decisions, complexity |
| L3: Detailed | Learning, debugging | Step-by-step walkthrough with reasoning |
| L4: Line-by-Line | Deep debugging, teaching | Every line explained with context |
Audience Calibration
| Audience | Assumes Knowledge Of | Explains |
|---|
| Beginner | Basic syntax, variables, functions | Data structures, patterns, language idioms |
| Intermediate | Common patterns, standard library | Architecture decisions, trade-offs, edge cases |
| Advanced | Design patterns, complexity theory | Non-obvious optimizations, subtle bugs, alternatives |
Workflow
Step 1: Read and Classify
Before explaining, identify:
- Language and framework — Determines idioms and conventions
- Purpose — What problem does this code solve?
- Patterns used — Design patterns, architectural patterns
- Complexity — Time and space complexity
- Dependencies — What external code does this rely on?
- Edge cases — What inputs could cause unexpected behavior?
Step 2: Explain at Requested Depth
L1: Overview
export function debounce<T extends (...args: unknown[]) => unknown>(
fn: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
L1 Explanation:
This is a debounce utility that delays function execution until a pause in calls. If the function is called repeatedly within the delay window, only the last call executes. Common use: preventing excessive API calls during user typing.
L2: Standard
L2 Explanation:
Purpose: Creates a debounced version of any function. The debounced function waits for delay milliseconds of inactivity before executing.
How it works:
- Returns a wrapper function that captures the original function in a closure
- Each call clears any pending timeout and sets a new one
- The original function only executes when no new calls arrive within the delay period
Pattern: Closure + Timer control (common in event handling)
Complexity: O(1) time per call, O(1) space (one timeout reference)
TypeScript notes: Generic type T preserves the original function's parameter types via Parameters<T>. Return type is void because the debounced function does not return the original's result (it executes asynchronously).
Common usage: Search input handlers, window resize listeners, form auto-save
L3: Detailed
L3 Explanation:
Context: Debouncing is a rate-limiting technique. Imagine a user typing in a search box. Without debouncing, every keystroke triggers an API call. With debouncing, the API call only fires after the user stops typing for a specified duration.
Step-by-step flow:
Call 1 at t=0ms: Clear timer → Set timer for t=300ms
Call 2 at t=100ms: Clear timer (cancels t=300ms) → Set timer for t=400ms
Call 3 at t=200ms: Clear timer (cancels t=400ms) → Set timer for t=500ms
[user stops typing]
t=500ms: Timer fires → Original function executes with Call 3's arguments
Closure analysis:
timeoutId lives in the closure created by debounce(), shared across all calls to the returned function
fn and delay are also captured in this closure
- Each new call to the returned function resets the shared
timeoutId
Type system breakdown:
T extends (...args: unknown[]) => unknown — T is any function type
Parameters<T> — Extracts T's parameter types as a tuple
ReturnType<typeof setTimeout> — Platform-agnostic timeout ID type (number in browser, NodeJS.Timeout in Node)
Potential issues:
- The return value of
fn is discarded (cannot await a debounced async function)
- No
cancel() method — once queued, the final call always executes
- No
flush() method — cannot force immediate execution
- Memory: the closure holds a reference to
fn and its bound context
Improved version would add:
const debounced = (...args) => { ... };
debounced.cancel = () => clearTimeout(timeoutId);
debounced.flush = () => { clearTimeout(timeoutId); fn(...lastArgs); };
return debounced;
L4: Line-by-Line
Line 1: export function debounce<T extends (...args: unknown[]) => unknown>(
│ export — Makes this function importable from other modules
│ function — Named function declaration (hoisted, better stack traces than arrow)
│ <T extends (...args: unknown[]) => unknown>
│ — Generic type parameter T, constrained to be any function
│ — This lets TypeScript infer the exact parameter types of the input function
Line 3: fn: T,
│ The original function to debounce, typed as T
Line 4: delay: number
│ Milliseconds to wait after last call before executing
Line 5: ): (...args: Parameters<T>) => void {
│ Return type: a function with same parameters as T, but returning void
│ (because execution is deferred, we can't return T's return value synchronously)
Line 6: let timeoutId: ReturnType<typeof setTimeout>;
│ Declared in outer scope — shared across all invocations of the returned function
│ ReturnType<typeof setTimeout> handles both browser (number) and Node (Timeout)
│ Using `let` because it will be reassigned on each call
Line 7: return (...args: Parameters<T>) => {
│ Return a new function that captures fn, delay, and timeoutId in its closure
Line 8: clearTimeout(timeoutId);
│ Cancel any previously scheduled execution
│ Safe to call even if timeoutId is undefined (clearTimeout handles it)
Line 9: timeoutId = setTimeout(() => fn(...args), delay);
│ Schedule fn to execute after `delay` ms
│ Arrow function captures `args` from this specific call
│ Assigns new timeout ID so next call can cancel it
Line 10: };
Line 11: }
Step 3: Identify Patterns and Concepts
When explaining code, call out recognized patterns:
Design Patterns:
- Singleton - Factory - Observer
- Strategy - Decorator - Adapter
- Command - State Machine - Builder
- Repository - Middleware chain - Pub/Sub
Algorithmic Patterns:
- Two pointers - Sliding window - Divide and conquer
- Dynamic programming - Greedy - Backtracking
- BFS/DFS - Binary search - Memoization
Architectural Patterns:
- MVC / MVVM - Event-driven - CQRS
- Microservices - Hexagonal - Layered
- Saga - Circuit breaker - Bulkhead
Step 4: Complexity Analysis
Always include when relevant:
Time Complexity:
Best case: O(...)
Average case: O(...)
Worst case: O(...)
Space Complexity: O(...)
Explanation: [Why this complexity, what dominates]
Example:
This function uses a hash map for O(1) lookups inside a loop that
iterates n items, giving O(n) total time. The hash map stores at
most n entries, so space is also O(n).
Compared to the naive O(n^2) nested loop approach, this trades
O(n) extra memory for O(n) time improvement.
Explanation Formatting
Structure explanations consistently:
## [Function/Module Name]
**Purpose:** One sentence.
**Pattern:** [Design pattern if applicable]
**Flow:**
1. First thing that happens
2. Second thing
3. Third thing
**Complexity:** Time O(n), Space O(1)
**Key Insight:** [The non-obvious thing that makes this work]
**Watch Out For:** [Edge cases, gotchas, limitations]
Visual Aids
For complex flows, include Mermaid diagrams:
flowchart TD
A[Function called] --> B{Timer active?}
B -->|Yes| C[Clear existing timer]
B -->|No| D[Skip clear]
C --> E[Set new timer]
D --> E
E --> F{Timer expires?}
F -->|New call arrives| B
F -->|No new calls| G[Execute original function]
For state machines:
stateDiagram-v2
[*] --> Idle
Idle --> Loading: fetch()
Loading --> Success: data received
Loading --> Error: request failed
Error --> Loading: retry()
Success --> Loading: refresh()
Anti-Patterns in Explanation
- Narrating syntax — "The
const keyword declares a constant" (reader knows this)
- Ignoring context — Explaining a function without explaining its role in the system
- Skipping the WHY — Describing what each line does without explaining why this approach was chosen
- Assuming knowledge — Using jargon without checking if the audience knows it
- Over-explaining — Giving L4 depth when L2 was requested
From api-documentation
API documentation best practices — Swagger UI, Redoc, interactive examples, and auto-generation from code.
API Documentation
Purpose
Produce clear, interactive, and maintainable API documentation. Covers OpenAPI spec authoring, Swagger UI and Redoc hosting, auto-generation from TypeScript/Python code, and documentation-as-code workflows.
Key Patterns
OpenAPI 3.1 Spec Structure
openapi: 3.1.0
info:
title: My API
version: 1.0.0
description: |
Production API for managing users and orders.
All endpoints require a Bearer token in the `Authorization` header.
- 100 requests/minute per API key
- 429 response when exceeded
contact:
email: api-support@example.com
servers:
- url: https://api.example.com/v1
description: Production
- url: https://staging-api.example.com/v1
description: Staging
paths:
/users:
get:
operationId: listUsers
summary: List
[, , ]
[]
Swagger UI Setup (Next.js)
import { NextResponse } from 'next/server';
import spec from '@/openapi.json';
export async function GET() {
return NextResponse.json(spec);
}
'use client';
import SwaggerUI from 'swagger-ui-react';
import 'swagger-ui-react/swagger-ui.css';
export default function ApiDocs() {
return (
<div className="py-16">
<SwaggerUI url="/api/docs" />
</div>
);
}
Redoc Setup
<!doctype html>
<html>
<head>
<title>API Reference</title>
<meta charset="utf-8" />
<link
href="https://fonts.googleapis.com/css?family=Inter:300,400,600"
rel="stylesheet"
/>
</head>
<body>
<redoc spec-url="/api/docs"></redoc>
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
</body>
</html>
'use client';
import { useEffect, useRef } from 'react';
export default function RedocDocs() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (window.Redoc && containerRef.current) {
window.Redoc.init('/api/docs', {
theme: {
colors: { primary: { main: '#3b82f6' } },
typography: { fontSize: '1rem', fontFamily: 'Inter, sans-serif' },
},
}, containerRef.current);
}
}, []);
return <div ref={containerRef} />;
}
Auto-Generation from TypeScript (Zod + OpenAPI)
import { z } from 'zod';
import { extendZodWithOpenApi, OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
extendZodWithOpenApi(z);
const registry = new OpenAPIRegistry();
const UserSchema = registry.register(
'User',
z.object({
id: z.string().openapi({ example: 'usr_abc123' }),
name: z.string().openapi({ example: 'Jane Doe' }),
email: z.string().email().openapi({ example: 'jane@example.com' }),
})
);
registry.registerPath({
method: 'get',
path: '/users',
summary: 'List all users',
tags: ['Users'],
request: {
query: z.object({
cursor: z.string().optional(),
limit: z.().().().().(),
}),
},
: {
: {
: ,
: {
: {
: z.({
: z.(),
: z.().(),
}),
},
},
},
},
});
{ } ;
generator = (registry.);
spec = generator.({
: ,
: { : , : },
: [{ : }],
});
Auto-Generation from Python (FastAPI)
from fastapi import FastAPI, Query
from pydantic import BaseModel, EmailStr
app = FastAPI(
title="My API",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json",
)
class User(BaseModel):
id: str
name: str
email: EmailStr
model_config = {
"json_schema_extra": {
"examples": [{"id": "usr_abc123", "name": "Jane Doe", "email": "jane@example.com"}]
}
}
class UserListResponse(BaseModel):
data: list[User]
next_cursor: str | None = None
@app.get("/users", response_model=UserListResponse, tags=["Users"])
async def list_users(
cursor: str | None = Query(None, description="Pagination cursor"),
limit: int = Query(20, ge=1, le=100, description=),
):
...
Documentation-as-Code CI Pipeline
name: API Docs
on:
push:
paths:
- 'src/api/**'
- 'openapi/**'
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate OpenAPI spec
run: npx tsx scripts/generate-openapi.ts > openapi.json
- name: Validate spec
run: npx @redocly/cli lint openapi.json
- name: Check for breaking changes
run: npx oasdiff breaking openapi-previous.json openapi.json
- name: Build Redoc static site
Inline Code Examples
Always include request/response examples in docs:
paths:
/users:
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUser'
examples:
basic:
summary: Minimal user creation
value:
name: "Jane Doe"
email: "jane@example.com"
full:
summary: User with all optional fields
value:
name: "Jane Doe"
email: "jane@example.com"
avatar_url: "https://example.com/avatar.jpg"
metadata:
source: "signup_form"
Best Practices
- Single source of truth — Generate docs from code (Zod schemas, Pydantic models, JSDoc) rather than maintaining a separate spec file.
- Include realistic examples — Every schema and endpoint should have concrete example values, not placeholder text.
- Document error responses — Show all possible error codes, not just the happy path.
- Use tags to group endpoints — Organize by resource (Users, Orders) not by HTTP method.
- Version your API docs — Docs should match the deployed API version; use CI to enforce spec validity.
- Add authentication instructions — Document how to obtain and use tokens at the top of the spec.
- Detect breaking changes in CI — Use
oasdiff or openapi-diff to catch breaking changes before merge.
- Provide SDKs or code snippets — Show curl, JavaScript, and Python examples for each endpoint.
Common Pitfalls
| Pitfall | Problem | Fix |
|---|
| Manually maintained spec | Drift between docs and implementation | Generate spec from code with CI validation |
| No examples | Users cannot understand expected shapes | Add example to every schema property |
| Missing error documentation | Users surprised by error formats | Document all error codes and response bodies |
| Stale docs after refactor | 404s and wrong schemas frustrate consumers | CI pipeline regenerates + validates on every push |
| Too many nested $refs | Swagger UI becomes hard to navigate | Inline small schemas; use $ref only for reusable types |
| No authentication docs | Users cannot make their first API call | Add auth section to spec info.description |
From openapi
OpenAPI 3.1 specification authoring, Swagger UI integration, client SDK generation, schema validation, and API-first design workflow
OpenAPI Specification Skill
Purpose
OpenAPI is the industry standard for describing REST APIs. This skill covers writing OpenAPI 3.1 specs, integrating interactive documentation (Swagger UI, Scalar), generating type-safe client SDKs, validating requests/responses against the spec, and embedding spec linting into CI. An API-first approach catches contract mismatches before code is written.
Key Concepts
OpenAPI 3.1 Structure
openapi: 3.1.0
info: → API metadata (title, version, description)
servers: → Base URLs for environments
paths: → Endpoints and their operations
/resource:
get: → Operation (method + path)
parameters: → Query, path, header, cookie params
requestBody: → Request payload schema
responses: → Response schemas by status code
components:
schemas: → Reusable data models (JSON Schema)
securitySchemes: → Auth definitions
parameters: → Reusable parameters
responses: → Reusable response objects
security: → Global security requirements
tags: → Logical grouping of operations
OpenAPI 3.1 vs 3.0
| Feature | 3.0 | 3.1 |
|---|
| JSON Schema compatibility | Subset (divergent) | Full JSON Schema 2020-12 |
type as array | Not supported | type: ["string", "null"] |
$ref siblings | Ignored | Allowed alongside $ref |
| Webhooks | Not supported | webhooks top-level key |
const keyword | Not supported | Supported |
Workflow
Step 1: Write the Spec (API-First)
openapi: 3.1.0
info:
title: Acme API
version: 1.0.0
description: |
The Acme API provides programmatic access to manage products,
orders, and customers.
contact:
name: API Support
email: api@acme.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.acme.com/v1
description: Production
- url: https://staging-api.acme.com/v1
description: Staging
- url: http://localhost:3000/api/v1
description: Local development
security:
- bearerAuth: []
tags:
- name: Products
description: Product catalog management
- name: Orders
[]
[, , ]
[]
[]
[, , , , ]
[, , ]
[, ]
[, ]
[, , ]
[, , ]
[, ]
[, , , ]
[]
[, ]
Step 2: Serve Interactive Documentation
Option A: Scalar (Modern, Recommended)
import { ApiReference } from '@scalar/nextjs-api-reference';
const config = {
spec: {
url: '/openapi.yaml',
},
theme: 'kepler',
layout: 'modern',
darkMode: true,
hideModels: false,
searchHotKey: 'k',
};
export const GET = ApiReference(config);
Option B: Swagger UI (Classic)
import { NextResponse } from 'next/server';
export async function GET() {
const html = `
<!DOCTYPE html>
<html>
<head>
<title>API Docs</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
<script>
SwaggerUIBundle({
url: '/openapi.yaml',
dom_id: '#swagger-ui',
deepLinking: true,
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
});
</script>
</body>
</html>`;
return new NextResponse(html, { headers: { 'Content-Type': 'text/html' } });
}
Step 3: Generate Type-Safe Client SDKs
npm install -D openapi-typescript openapi-fetch
npx openapi-typescript ./public/openapi.yaml -o ./src/lib/api/schema.d.ts
Type-Safe API Client
import createClient from 'openapi-fetch';
import type { paths } from './schema';
export const api = createClient<paths>({
baseUrl: process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000/api/v1',
headers: {
'Content-Type': 'application/json',
},
});
api.use({
async onRequest({ request }) {
const token = getAuthToken();
if (token) {
request.headers.set('Authorization', `Bearer ${token}`);
}
return request;
},
});
const { data, error } = await api.GET('/products', {
params: {
query: { category: 'electronics', page: 1, limit: 20 },
},
});
const { data: product } = await api.(, {
: {
: ,
: ,
: ,
},
});
Step 4: Request Validation Middleware
import { OpenAPIValidator } from 'openapi-backend';
import spec from '../../public/openapi.json';
const validator = new OpenAPIValidator({ definition: spec });
export async function validateRequest(
req: Request,
operationId: string
): Promise<{ valid: boolean; errors?: string[] }> {
const body = req.method !== 'GET' ? await req.json() : undefined;
const url = new URL(req.url);
const query = Object.fromEntries(url.searchParams);
const result = validator.validateRequest(
{
method: req.method.toLowerCase(),
path: url.pathname,
body,
query,
headers: Object.fromEntries(req.headers),
},
operationId
);
(result.) {
{
: ,
: result..( ),
};
}
{ : };
}
Step 5: Lint the Spec in CI
name: Lint OpenAPI Spec
on:
pull_request:
paths:
- 'public/openapi.yaml'
- 'openapi/**'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint OpenAPI spec
uses: stoplightio/spectral-action@v0.8.11
with:
file_glob: 'public/openapi.yaml'
spectral_ruleset: .spectral.yaml
- name: Validate spec
run: npx @redocly/cli lint public/openapi.yaml
.spectral.yaml (Linting Rules)
extends:
- spectral:oas
rules:
operation-operationId: error
operation-description: warn
operation-tags: error
oas3-schema: error
info-contact: warn
no-$ref-siblings: off
require-pagination:
description: List endpoints must include pagination parameters
given: "$.paths[*].get.parameters"
severity: warn
then:
function: schema
functionOptions:
schema:
type: array
contains:
properties:
name:
enum: [page, cursor, offset]
Best Practices
- API-first design — Write the spec before the implementation. The spec is the contract; code is the implementation detail.
- Use
$ref aggressively — Extract every schema, parameter, and response into components/. Duplication in specs causes drift.
- Always include
operationId — Client generators use this as the method name. Choose clear, unique names (e.g., listProducts, createOrder).
- Document error responses — Every endpoint should document 401, 404, 422, and 500 responses with example payloads.
- Use
openapi-fetch over hand-rolled clients — It provides compile-time type safety derived directly from the spec, eliminating type drift.
- Version via URL path —
/v1/products is clearer than header-based versioning for most APIs.
- Add examples to schemas — Swagger UI and Scalar render examples in the docs, making them immediately useful for consumers.
- Lint in CI — Use Spectral or Redocly CLI to catch spec issues before merge.
Common Pitfalls
| Pitfall | Symptom | Fix |
|---|
| Spec diverges from implementation | Clients get unexpected responses | Generate types from spec and validate requests against it at runtime |
Missing operationId | Generated client methods named getPathProducts instead of listProducts | Add operationId to every operation |
Nullable fields using nullable: true | Validation errors in OpenAPI 3.1 | Use type: ["string", "null"] instead of nullable: true (3.0 syntax) |
Circular $ref breaking generators | Code generation crashes or produces infinite types | Break cycles with intermediate schemas or use generators that handle cycles (openapi-typescript does) |
| No pagination on list endpoints | Clients fetch unbounded datasets | Always include page/limit or cursor-based pagination parameters |
| Hardcoded server URLs | Spec unusable across environments | Use servers array with dev/staging/prod URLs; support env variable override in clients |
| Giant monolithic spec file | Unreadable, merge conflicts | Split into multiple files using $ref: './schemas/product.yaml' and bundle at build time with @redocly/cli bundle |
Missing required arrays | Clients treat all fields as optional | Explicitly list required fields on every object schema |