code-documentation

SKILL.md

Code Documentation Skill

Why this matters

In AI-assisted development, code is written fast but read many times — by humans doing code review, by other AI agents iterating on the codebase, and by documentation generators building system docs. Poorly documented code forces every future reader to reverse-engineer intent from implementation, which is slow, error-prone, and expensive.

Well-documented code serves three audiences simultaneously:

  1. Human developers — understand what the code does and why, without reading every line
  2. AI agents — get the context they need to make safe, accurate modifications
  3. Documentation systems — extract structured metadata to auto-generate API docs, architecture diagrams, and onboarding guides

The cost of documenting at write-time is near zero (especially with AI assistance). The cost of not documenting is paid every single time someone touches that code again.


Core Principles

1. Document Intent, Not Mechanics

Comments should answer why, not what. The code already says what it does. The comment explains the reasoning, the constraints, the business rule, or the non-obvious decision.

// BAD — restates the code
// Set timeout to 5000ms
const TIMEOUT = 5000;

// GOOD — explains the reasoning
// Payment gateway requires responses within 5s or it drops the connection.
// We set our timeout slightly below to have room for cleanup.
const TIMEOUT = 5000;

2. Every File Has a Header

Every source file starts with a block comment that answers: what is this file, why does it exist, and who/what consumes it. This is the single most important documentation a file can have — it's the first thing a human or AI reads, and it sets the context for everything below.

/**
 * @file PaymentProcessor
 * @description Handles payment lifecycle: authorization, capture, and refund.
 *   Wraps the Stripe SDK with retry logic and idempotency keys to survive
 *   transient failures. Used by the checkout flow and the admin refund panel.
 *
 * @module payments
 * @dependencies stripe, @lib/retry, @lib/logger
 *
 * @architecture
 *   - Exposes a stateless service class (no internal state between calls)
 *   - All methods are idempotent — safe to retry on failure
 *   - Errors are wrapped in PaymentError for uniform handling upstream
 *
 * @seeAlso
 *   - docs/architecture/payment-flow.md — sequence diagram
 *   - CheckoutController — primary consumer
 */

3. Functions Are Self-Documenting Units

Every exported function or method gets a JSDoc/TSDoc block. Internal helper functions get one too if their purpose isn't immediately obvious from name + params.

/**
 * Attempts to authorize a payment, retrying on transient Stripe errors.
 *
 * Returns the authorized payment intent on success. Throws PaymentError
 * with code 'CARD_DECLINED' or 'GATEWAY_TIMEOUT' on permanent failures.
 *
 * @param amount - Charge amount in cents (integer, no decimals)
 * @param currency - ISO 4217 currency code (e.g., 'usd', 'ars')
 * @param customerId - Stripe customer ID, must start with 'cus_'
 * @param idempotencyKey - Client-generated UUID to prevent double charges
 *
 * @returns The Stripe PaymentIntent object with status 'requires_capture'
 * @throws {PaymentError} When the card is declined or gateway times out
 *
 * @example
 * const intent = await authorize(5000, 'usd', 'cus_abc123', uuidv4());
 * // intent.status === 'requires_capture'
 */
async function authorize(
  amount: number,
  currency: string,
  customerId: string,
  idempotencyKey: string
): Promise<Stripe.PaymentIntent> {

4. Types Are Documentation

In TypeScript, interfaces and type aliases are first-class documentation. Document them with the same care as functions — they're the contract other code relies on.

/**
 * Represents a user's subscription state.
 *
 * The subscription lifecycle goes: trial → active → (past_due → active | canceled).
 * A subscription can also go directly from trial to canceled if the user never
 * adds a payment method.
 *
 * @see SubscriptionService.transition() for the state machine implementation
 */
interface Subscription {
  /** Unique subscription ID (prefixed 'sub_') */
  id: string;

  /** Current lifecycle state — see type comment for valid transitions */
  status: 'trial' | 'active' | 'past_due' | 'canceled';

  /** ISO 8601 timestamp. Null only for trial subscriptions. */
  currentPeriodEnd: string | null;

  /**
   * Stripe price ID for the current plan.
   * Changes on upgrade/downgrade take effect at currentPeriodEnd.
   */
  priceId: string;
}

5. Comments Age — Keep Them Honest

A wrong comment is worse than no comment. When modifying code, always check if nearby comments are still accurate. If a comment describes behavior that the code no longer implements, update or remove it immediately.


Documentation Checklist

Apply this checklist to every file, whether written by a human or generated by AI:

File Level

  • File header with @file, @description, @module
  • Dependencies listed (not just imports — explain why each external dep is needed if non-obvious)
  • Architecture notes if the file has non-trivial structure (state machines, event flows, caching)
  • @seeAlso links to related files, docs, or specs when they exist

Function / Method Level

  • JSDoc/TSDoc block on every exported function
  • @param for each parameter with type and constraints (ranges, formats, valid values)
  • @returns with description of success case
  • @throws for each error type with when/why it's thrown
  • @example for functions with non-trivial usage patterns
  • For private helpers: at minimum a one-line comment explaining purpose

Type / Interface Level

  • Doc block on the type/interface explaining its role and lifecycle
  • Individual field comments for anything that isn't self-explanatory from the name
  • Enum values documented if their meaning isn't obvious from the name

Inline Comments

  • Business rules explained where they're enforced (// HIPAA requires we mask the last 4 digits)
  • Non-obvious algorithms or performance choices annotated
  • Workarounds or hacks flagged with // HACK: or // WORKAROUND: and a brief explanation
  • TODOs formatted as // TODO(owner): description — ticket/issue link
  • Edge cases called out (// Empty array is valid here — means "all categories")

React Components

  • Component-level JSDoc explaining what it renders and when to use it
  • Props interface fully documented (see Types section above)
  • Complex hooks explained: why this hook, what triggers re-renders, cleanup behavior
  • Conditional rendering logic commented if it's based on business rules

Patterns by File Type

For detailed examples and templates for each file type in a JS/TS stack, read the reference file:

references/patterns.md — Complete templates for:

  • React components (functional, with hooks)
  • Express/Node API routes
  • Service classes
  • Utility / helper modules
  • Database models and migrations
  • Configuration files
  • Test files
  • Scripts and CLI tools

Anti-Patterns

These are common in AI-generated code and must be caught in review:

Parrot comments — Restating the code in English adds zero value.

// BAD
// Increment counter by one
counter++;

// GOOD — no comment needed, the code is self-explanatory
counter++;

Missing "why" on magic values — Every literal that isn't 0, 1, true, false, or an empty string needs either a named constant or an inline explanation.

// BAD
if (retries > 3) { ... }

// GOOD
const MAX_RETRIES = 3; // Stripe recommends max 3 retries for idempotent requests
if (retries > MAX_RETRIES) { ... }

Orphan TODOs — A TODO without an owner or link is just noise that never gets resolved.

// BAD
// TODO: fix this later

// GOOD
// TODO(fran): Handle currency conversion for ARS — see JIRA-1234

Stale comments — Comments that describe behavior the code no longer exhibits. If you change logic, update or remove the comment in the same commit.

Over-documentation — Documenting every single line is noise. Self-explanatory code (well-named variables, small functions, clear types) doesn't need inline comments. The goal is to document what can't be expressed through code alone.


Comment Tags Reference

Use these consistently across the codebase. AI agents and documentation tools can parse them:

Tag Meaning Example
TODO(owner) Planned work, not urgent // TODO(fran): Add pagination — JIRA-456
FIXME(owner) Known bug, needs attention // FIXME(leo): Race condition on concurrent saves
HACK Intentional shortcut, explain why // HACK: Stripe SDK v12 has a bug with ARS, hardcoding rate
WORKAROUND External constraint forced this // WORKAROUND: Chrome 120 broke flexbox in this layout
PERF Performance-related decision // PERF: Memoized because this runs on every keystroke
SECURITY Security-sensitive code // SECURITY: Sanitize before DB query to prevent injection
DEPRECATED Scheduled for removal // DEPRECATED: Use v2/checkout instead — removing in Q2
AI-CONTEXT Extra context specifically for AI agents // AI-CONTEXT: This ordering matters — events must process sequentially

The AI-CONTEXT tag is new and specific to AI-assisted workflows. Use it to leave breadcrumbs that help an AI agent understand constraints that aren't obvious from the code structure alone.


For AI Code Generation

When an AI generates code (via Copilot, Cursor, Claude, or any agent), the output MUST meet these documentation standards before it's considered "done." This means:

  1. The prompt or spec should request documented output. Don't assume the AI will document by default — be explicit. Example: "Generate with full JSDoc, file header, and inline comments explaining business logic."

  2. Review AI output for parrot comments. AI models tend to generate comments that restate the code. These should be caught and either upgraded to explain "why" or removed.

  3. Verify type documentation. AI sometimes generates correct types but skips the doc comments on interfaces. Every field in a shared type needs a comment.

  4. Check for missing edge-case comments. AI-generated code often handles edge cases correctly but doesn't explain why the edge case exists or matters.

  5. Add architecture context. AI doesn't know your system's architecture. After generating, add the @seeAlso, @module, and @architecture notes that connect this code to the broader system.


Documentation as System Input

Well-documented code is a data source for generating system documentation:

  • API documentation — JSDoc @param, @returns, @throws map directly to API reference docs
  • Architecture docs — File headers with @module and @architecture feed into system diagrams
  • Onboarding guides@example blocks become the basis for getting-started tutorials
  • Change logs@deprecated and @seeAlso track migration paths
  • Agent contextAI-CONTEXT tags feed directly into the context an orchestrator provides to sub-agents

When documentation lives in the code (not in a separate wiki that gets stale), it stays accurate because it's updated in the same PR that changes the behavior.


Enforcement

Documentation quality should be enforced at three levels:

  1. At generation time — Specs and prompts explicitly require documentation standards
  2. At review time — PR checklist includes documentation coverage
  3. At CI time — Lint rules catch missing JSDoc on exports (e.g., eslint-plugin-jsdoc)

See references/eslint-config.md for the recommended ESLint configuration.

Weekly Installs
1
First Seen
3 days ago
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1