
documentation-writing
by tejovanthn
SKILL.md
name: documentation-writing description: Best practices for creating clear, comprehensive, and maintainable documentation for code, APIs, and systems
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
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です