Documentation is the work developers put off until last — and then write poorly because they’re tired. Claude Code is excellent for documentation tasks because it reads the actual code to write accurate docs rather than guessing. It generates READMEs that include every configuration option, JSDoc that matches the function signatures, and OpenAPI schemas that match your actual route handlers.
This guide covers documentation workflows with Claude Code: README files, API documentation with OpenAPI/Swagger, inline code documentation, Storybook stories, and keeping docs in sync with code changes.
README Files
Project README
Write a README for this project.
Read the codebase and generate: overview, quickstart,
configuration options, and all API endpoints.
Include: prerequisites, installation, development setup,
environment variables.
Claude reads package.json, your route files, environment config, and generates accurate documentation:
# project-name
Brief description of what this does.
## Prerequisites
- Node.js 20+
- PostgreSQL 16+
- Redis 7+
## Quick Start
```bash
git clone https://github.com/org/project-name
cd project-name
cp .env.example .env # Fill in required values
npm install
npm run db:migrate
npm run dev
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL | Yes | — | PostgreSQL connection string |
JWT_SECRET | Yes | — | Secret for JWT signing (min 32 chars) |
REDIS_URL | No | redis://localhost:6379 | Redis connection string |
LOG_LEVEL | No | info | Log verbosity (debug/info/warn/error) |
PORT | No | 3000 | HTTP server port |
API Endpoints
Authentication
POST /api/auth/register — Create a new user account
Body: { email, password, name }
Returns: { user, accessToken, refreshToken }
POST /api/auth/login — Authenticate
Body: { email, password }
Returns: { user, accessToken, refreshToken }
Rather than writing this from memory, Claude reads:
- Route files to find all endpoints
- Validation schemas for parameter documentation
- `.env.example` for the environment variable table
- `package.json` for version requirements
The result is documentation that's accurate at generation time — which is usually the most accurate a README will ever be.
### Keeping READMEs Updated
The README documents 8 environment variables but the codebase now uses 12. Update the README to match reality.
Claude scans for `process.env.` references in the codebase, compares to the README's environment variable table, and adds the missing entries with descriptions inferred from how they're used.
## OpenAPI / Swagger Documentation
### Generating from Code
Generate an OpenAPI 3.1 spec for our Express API. Read the route files and generate the full spec with:
- All endpoints with HTTP methods
- Request body schemas from our Zod validators
- Response schemas
- Security requirements
- Error responses
Claude reads the zod schemas, route definitions, and generates the OpenAPI YAML:
```yaml
openapi: 3.1.0
info:
title: Acme API
version: 1.0.0
description: |
REST API for Acme platform.
## Authentication
All endpoints except /api/auth/* require a Bearer token.
Obtain tokens via POST /api/auth/login.
security:
- bearerAuth: []
paths:
/api/orders:
get:
summary: List orders
operationId: listOrders
parameters:
- name: page
in: query
schema:
type: integer
minimum: 1
default: 1
- name: status
in: query
schema:
$ref: '#/components/schemas/OrderStatus'
responses:
'200':
description: Paginated order list
content:
application/json:
schema:
type: object
properties:
orders:
type: array
items:
$ref: '#/components/schemas/Order'
pagination:
$ref: '#/components/schemas/Pagination'
'401':
$ref: '#/components/responses/Unauthorized'
post:
summary: Create order
operationId: createOrder
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderRequest'
responses:
'201':
description: Order created
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'422':
$ref: '#/components/responses/ValidationError'
components:
schemas:
OrderStatus:
type: string
enum: [pending, confirmed, shipped, delivered, cancelled]
Order:
type: object
required: [id, status, totalCents, createdAt]
properties:
id:
type: string
format: uuid
status:
$ref: '#/components/schemas/OrderStatus'
totalCents:
type: integer
description: Total amount in cents
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
Once the spec exists, Claude maintains it as routes change — “I added a filter parameter to GET /api/orders, update the spec.”
Annotating Existing Code for OpenAPI
For frameworks with decorator-based OpenAPI generation (NestJS, fastify-swagger):
Add @ApiProperty decorators to this NestJS DTO class
so it appears correctly in Swagger UI.
Add examples for all fields.
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class CreateOrderDto {
@ApiProperty({
description: 'Array of items to order',
type: [OrderItemDto],
minItems: 1,
maxItems: 50,
})
items: OrderItemDto[];
@ApiProperty({
description: 'Shipping address',
example: '123 Main St, San Francisco, CA 94105',
})
shippingAddress: string;
@ApiPropertyOptional({
description: 'Discount coupon code',
example: 'SUMMER20',
})
couponCode?: string;
}
JSDoc and TSDoc
Inline Documentation
Add JSDoc comments to the public methods of this service class.
Include: description, all params, return type, and examples.
Don't add docs to private methods.
/**
* Creates a new order from the provided items.
* Validates inventory, calculates total, and saves to the database.
*
* @param userId - ID of the authenticated user creating the order
* @param items - Array of order line items with product IDs and quantities
* @param options - Optional order configuration
* @returns The created order with computed total and assigned ID
* @throws {InsufficientInventoryError} When any item's quantity exceeds available stock
* @throws {ProductNotFoundError} When an item references a non-existent product
*
* @example
* const order = await orderService.createOrder('user-123', [
* { productId: 'prod-456', quantity: 2 },
* { productId: 'prod-789', quantity: 1 },
* ]);
* console.log(order.totalCents); // 5999
*/
async createOrder(
userId: string,
items: OrderItem[],
options: OrderOptions = {}
): Promise<Order> {
// implementation
}
Claude reads existing method signatures and generates documentation that matches the actual behavior, not generic placeholder text. It identifies which methods are public vs. private and only documents the public interface.
Extracting Documentation from Tests
The tests for UserService describe the expected behavior.
Generate JSDoc for the UserService methods from the test descriptions.
Claude reads the test file, finds the describe/it blocks, and generates JSDoc that matches the tested behavior. This is particularly useful for functions that have edge cases documented in tests but not in the source.
Storybook Stories
Write Storybook stories for the Button component.
Cover all variants (primary, secondary, danger), all sizes,
disabled state, loading state, and with icons.
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
parameters: {
layout: 'centered',
},
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
},
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: { variant: 'primary', children: 'Click me' },
};
export const Danger: Story = {
args: { variant: 'danger', children: 'Delete item' },
};
export const Loading: Story = {
args: { variant: 'primary', children: 'Saving...', isLoading: true },
};
export const Disabled: Story = {
args: { variant: 'primary', children: 'Unavailable', disabled: true },
};
export const AllVariants: Story = {
render: () => (
<div style={{ display: 'flex', gap: '8px', flexDirection: 'column' }}>
{(['primary', 'secondary', 'danger'] as const).map(variant => (
<div key={variant} style={{ display: 'flex', gap: '8px' }}>
{(['sm', 'md', 'lg'] as const).map(size => (
<Button key={size} variant={variant} size={size}>
{variant} {size}
</Button>
))}
</div>
))}
</div>
),
};
Claude reads the component’s TypeScript interface to know which props to document and generates the argTypes that match the actual prop types.
Changelog and Migration Guides
We're releasing v2.0 with breaking changes.
List of what changed: [paste git log or diff]
Generate a migration guide for users upgrading from v1.
Claude reads the actual changes (removed functions, renamed parameters, changed return types) and generates:
- A
CHANGELOG.mdentry with the semantic versioning sections - A migration guide with before/after code examples for each breaking change
This is especially useful for library maintainers — the migration guide is accurate because it’s generated from the actual diff, not from memory.
Documentation Workflows
The documentation workflow that works well with Claude Code:
- On feature completion: “Document this function/class/API endpoint”
- On breaking changes: “Generate a migration guide for this change”
- On PR review: “Check if the README needs updating for these changes”
- On new project setup: “Generate a project README from the codebase”
Documentation quality degrades over time because code changes but docs don’t get updated. Claude Code’s ability to read the actual code means each documentation update starts from ground truth rather than outdated context.
For comprehensive documentation skill sets — technical writing patterns, changelog templating, and API reference generation — the Claude Skills 360 bundle includes documentation-specific skills. See the code review guide for adding documentation checks to your PR review workflow. See the CLAUDE.md guide for how context files reduce the need for repetitive documentation explanation. Start with the free tier.