documentation-writing
SKILL.md
Documentation Writing
This skill covers best practices for creating effective documentation that developers actually want to read and maintain.
Core Philosophy
Great documentation is:
- Clear: Easy to understand on first reading
- Complete: Answers the questions users actually have
- Current: Kept in sync with code changes
- Concise: No unnecessary fluff
- Scannable: Easy to find what you need
- Practical: Focuses on how-to, not just what
Types of Documentation
1. README Files
The front door to your project:
# Project Name
Brief description (1-2 sentences) of what this does.
## Features
- Key feature 1
- Key feature 2
- Key feature 3
## Quick Start
\`\`\`bash
npm install
npm run dev
\`\`\`
Visit http://localhost:3000
## Project Structure
\`\`\`
src/
routes/ # Remix routes
lib/ # Shared utilities
components/ # React components
\`\`\`
## Documentation
- [Setup Guide](docs/setup.md)
- [Architecture](docs/architecture.md)
- [API Reference](docs/api.md)
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)
## License
MIT
Key elements:
- What it does (immediately)
- How to get started (quickly)
- Where to find more information
- How to contribute
2. Code Comments
Use sparingly and wisely:
✅ Do explain WHY:
// We batch writes to avoid hitting DynamoDB's 25-item transaction limit
const batches = chunk(items, 25);
// Disable cache for this endpoint because user data changes frequently
// and stale data causes support tickets. See issue #123
export const loader = async () => {
// ...
};
✅ Do document complex algorithms:
/**
* Implements the Luhn algorithm for credit card validation.
* https://en.wikipedia.org/wiki/Luhn_algorithm
*/
function validateCardNumber(cardNumber: string): boolean {
// Double every second digit from right to left
// If doubling results in two digits, add them together
// ...
}
❌ Don't state the obvious:
// Get the user
const user = await getUser(id);
// Check if user exists
if (!user) {
// Return error
return error("Not found");
}
3. API Documentation
Document public APIs thoroughly:
/**
* Creates a new user account and sends a welcome email.
*
* @param email - User's email address (must be unique)
* @param password - Plain text password (will be hashed)
* @param name - User's display name
* @returns The created user object (without password)
* @throws {ValidationError} If email is invalid or already exists
* @throws {EmailError} If welcome email fails to send
*
* @example
* ```typescript
* const user = await createUser({
* email: "user@example.com",
* password: "secure123",
* name: "John Doe"
* });
* ```
*/
export async function createUser(
email: string,
password: string,
name: string
): Promise<User> {
// Implementation
}
4. Architecture Documentation
Explain the big picture:
# Architecture Overview
## System Components
### Frontend (Remix)
- Server-side rendering for initial load
- Progressive enhancement for interactivity
- Nested routes for UI composition
### Backend (SST)
- Lambda functions for API endpoints
- DynamoDB for data storage (single table design)
- S3 for file uploads
- SES for email sending
### Data Flow
1. User submits form → Remix action
2. Action validates data
3. Action calls business logic in `src/lib/`
4. Business logic updates DynamoDB
5. Action redirects or returns errors
6. Loader refetches data
7. Component renders updated state
## Key Design Decisions
### Why Single Table DynamoDB?
We use single table design because:
- Lower costs (one table vs many)
- Better performance (no joins needed)
- Atomic transactions across entities
- Aligns with serverless architecture
See: docs/dynamodb-design.md
### Why SST over CDK?
- Type-safe resource bindings
- Better developer experience
- Simpler infrastructure code
- Great local development story
5. Setup Documentation
Make it easy for new developers:
# Setup Guide
## Prerequisites
- Node.js 20+
- AWS account with CLI configured
- GitHub account (for deployment)
## Installation
1. Clone the repository:
\`\`\`bash
git clone https://github.com/your-org/project
cd project
\`\`\`
2. Install dependencies:
\`\`\`bash
npm install
\`\`\`
3. Set up environment:
\`\`\`bash
cp .env.example .env
# Edit .env and add your values
\`\`\`
4. Start SST:
\`\`\`bash
npm run sst:dev
\`\`\`
5. In another terminal, start Remix:
\`\`\`bash
npm run dev
\`\`\`
6. Visit http://localhost:3000
## Troubleshooting
### "Cannot find module 'sst'"
Run `npm install` again. SST might not have installed correctly.
### Port 3000 already in use
Kill the process using port 3000 or change the port in package.json.
6. ADR (Architecture Decision Records)
Document important decisions:
# ADR 001: Use Single Table Design for DynamoDB
## Status
Accepted
## Context
We need to store users, posts, comments, and relationships between them.
Traditional approach would be separate tables, but we're building a serverless app.
## Decision
We will use single table design with generic pk/sk keys.
## Consequences
### Positive
- Lower costs (one table vs many)
- Better query performance (no joins)
- Atomic transactions across entity types
- Simpler infrastructure
### Negative
- Higher initial learning curve
- Requires understanding access patterns upfront
- More complex to query during development
## Alternatives Considered
- Multiple tables (rejected: higher costs, no cross-table transactions)
- Relational DB (rejected: doesn't fit serverless model well)
Documentation Patterns
Pattern 1: Tutorial-Style Guides
Walk through a complete example:
# Building Your First Feature
Let's build a simple blog post feature together.
## Step 1: Create the Database Schema
First, we'll define how posts are stored in DynamoDB.
A post has:
- ID (unique identifier)
- Author (user who created it)
- Title
- Content
- Created date
In single table design, we'll store this as:
\`\`\`typescript
{
pk: "POST#<postId>",
sk: "METADATA",
authorId: "<userId>",
title: "My First Post",
content: "Hello, world!",
createdAt: "2025-01-02T10:00:00Z"
}
\`\`\`
## Step 2: Create the Route
Create a new file: `app/routes/posts.new.tsx`
\`\`\`typescript
// ... code here
\`\`\`
## Step 3: Add the Form
// ... continue the tutorial
Pattern 2: Reference Documentation
Quick lookup for APIs:
# Database API Reference
## `createPost()`
Creates a new blog post.
**Signature:**
\`\`\`typescript
createPost(authorId: string, data: PostData): Promise<Post>
\`\`\`
**Parameters:**
- `authorId` - ID of the user creating the post
- `data.title` - Post title (required, max 200 chars)
- `data.content` - Post content (required, max 50000 chars)
**Returns:**
- Promise resolving to created Post object
**Throws:**
- `ValidationError` - If data is invalid
- `DatabaseError` - If write fails
**Example:**
\`\`\`typescript
const post = await createPost("user123", {
title: "Hello World",
content: "My first post!"
});
\`\`\`
Pattern 3: Troubleshooting Guides
Help users solve common problems:
# Troubleshooting Guide
## Common Issues
### SST Deploy Fails with "Resource already exists"
**Symptoms:**
\`\`\`
Error: Resource MyFunction already exists
\`\`\`
**Cause:**
A previous deployment failed partway through.
**Solution:**
1. Delete the CloudFormation stack manually:
\`\`\`bash
aws cloudformation delete-stack --stack-name my-app-dev
\`\`\`
2. Wait for deletion to complete
3. Deploy again: `npm run deploy`
### DynamoDB Query Returns No Results
**Symptoms:**
Query runs without errors but returns empty array.
**Common Causes:**
1. **Wrong key format** - Check your pk/sk format matches
2. **Using scan instead of query** - Use Query for single partition
3. **GSI not ready** - Wait a few seconds after creating items
**Debug Steps:**
1. Log the query parameters
2. Check item in DynamoDB console
3. Verify key format matches exactly
Best Practices
1. Keep Docs Close to Code
src/
lib/
email/
send.ts
README.md # Email system docs
examples.md # Usage examples
2. Use Markdown Formatting
# Headers for sections
## Subheaders for subsections
**Bold** for emphasis
*Italic* for terms
`code` for inline code
\`\`\`typescript
// code blocks
\`\`\`
> Blockquotes for notes
- Bullet lists
- For multiple items
1. Numbered lists
2. For sequential steps
3. Include Code Examples
Always show working code:
## Creating a User
\`\`\`typescript
import { createUser } from "./lib/users";
const user = await createUser({
email: "user@example.com",
password: "secure123",
name: "John Doe"
});
console.log(`Created user: ${user.id}`);
\`\`\`
4. Link Related Documentation
See also:
- [Authentication Guide](./auth.md)
- [Database Schema](./database.md)
- [API Reference](./api.md)
5. Keep It Current
<!-- Add a "Last Updated" note -->
> Last Updated: 2025-01-02
> Version: 2.0.0
Documentation Tools
JSDoc for TypeScript
/**
* Represents a blog post.
*/
export interface Post {
/** Unique identifier */
id: string;
/** Post title (max 200 characters) */
title: string;
/** Post content in Markdown format */
content: string;
/** ISO 8601 timestamp of creation */
createdAt: string;
/** ID of the author */
authorId: string;
}
README Badges



Diagrams with Mermaid
\`\`\`mermaid
graph TD
A[User submits form] --> B[Remix action]
B --> C{Valid data?}
C -->|Yes| D[Save to DB]
C -->|No| E[Return errors]
D --> F[Redirect to success]
E --> G[Show form with errors]
\`\`\`
Documentation Checklist
When documenting a new feature:
- Update README if user-facing
- Add JSDoc comments to public APIs
- Create usage examples
- Document configuration options
- Explain error messages
- Add troubleshooting tips
- Link to related docs
- Update architecture diagrams
- Review for clarity
- Test all code examples
Common Mistakes to Avoid
❌ Don't:
- Write docs that are out of date
- Use jargon without explanation
- Skip error handling in examples
- Document internal implementation details
- Write novels (keep it concise)
- Assume prior knowledge
✅ Do:
- Update docs with code changes
- Define terms on first use
- Show how to handle errors
- Document public APIs and behavior
- Be clear and direct
- Provide context and examples
Documentation as Code
Treat docs like code:
// docs/examples/create-user.test.ts
// This file is both docs and tests!
import { createUser } from "../src/lib/users";
test("creating a user", async () => {
// This example appears in docs
const user = await createUser({
email: "user@example.com",
password: "secure123",
name: "John Doe"
});
expect(user.email).toBe("user@example.com");
expect(user.name).toBe("John Doe");
});
Further Reading
- Write The Docs: https://www.writethedocs.org/
- MDN Writing Guidelines: https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines
- Google Developer Documentation Style Guide: https://developers.google.com/style
Weekly Installs
6
Repository
tejovanthn/rasikalifeFirst Seen
8 days ago
Security Audits
Installed on
opencode6
gemini-cli6
claude-code6
github-copilot6
codex6
kimi-cli6