# Docstring Writer

> Add good docstrings to functions, classes, and modules that lack them. Uses the language-appropriate style (Google/numpydoc/JSDoc/rustdoc/godoc). Triggers on "add docstrings", "document this function/class/module", "write docstrings", or when the user is polishing a file for release.

- Skill: `duckcode-js/docstring-writer` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add duckcode-js/docstring-writer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/duckcode-js/docstring-writer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: DuckCode-js (https://skillmd.com/u/duckcode-js)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/duckcode-js/docstring-writer

---


# docstring-writer

Add clear, correctly-formatted docstrings to public functions and classes
that don't have them. Language-aware — uses the right convention for the
codebase automatically.

## When to use this

- "Add docstrings to this file"
- "Document this function"
- "Write docstrings for the module"
- "Add JSDoc to X"
- User is prepping a file for release / open-source / handoff

## Procedure

### 1. Identify the language and convention

From file extension and idioms in the file:

- **Python** — default to Google-style docstrings unless the codebase clearly
  uses NumPy style (look at existing docstrings in the repo). Fall back to
  reST/Sphinx only if the project has explicit Sphinx setup.
- **JavaScript / TypeScript** — JSDoc / TSDoc. Prefer TSDoc if `tsconfig.json`
  exists.
- **Rust** — rustdoc (`///`)
- **Go** — godoc (comment starts with the identifier name, e.g. `// Foo does X.`)
- **Java** — Javadoc
- **Ruby** — YARD or RDoc (check for existing conventions)
- **C / C++** — Doxygen (`/** ... */` or `///`) — check for existing convention
- **Other** — ask the user

If the file already has some docstrings, match their style exactly. Don't
mix conventions in one file.

### 2. Decide what to document

Document by default:

- Public functions, methods, classes (exported / non-underscore-prefixed
  in Python / uppercase in Go)
- Module-level docstring at the top of the file
- Non-obvious internal helpers if the user asks

Skip by default (only add if asked):

- Trivial getters/setters
- Auto-generated code (`# generated`, `// AUTO-GENERATED`)
- Test files (unless testing infrastructure, not test cases)
- Private methods (`_foo` in Python, unexported in Go)
- `__init__` in Python if it takes no meaningful args

### 3. Write the docstring

Every docstring answers three questions:

1. **What** does this do? (Summary line, imperative present tense, ≤ 80 chars)
2. **What are its inputs** and what do they mean? (Params section)
3. **What does it return / raise / yield** and under what conditions?

Add these when they add real value:

- **Example** — for anything non-trivial or with a surprising API. One
  small example, not a tutorial.
- **Warnings / gotchas** — thread-safety, side effects, mutation of arguments,
  performance characteristics if non-obvious
- **See also** — if it pairs with another function the reader should know about

Do NOT write:

- Docstrings that restate the function signature: `def add(a, b): """Add a and b."""`
- Docstrings that describe the implementation instead of the interface:
  `"""Uses a hashmap and then sorts by frequency."""` — implementation belongs in
  a comment inside the function, not the docstring.
- Docstrings that lie about behavior (e.g. copied from a similar function). Read
  the actual code.

### 4. Style-specific formatting

**Python / Google style (default for Python):**

```python
def parse_config(path: Path, *, strict: bool = False) -> Config:
    """Load a config file from disk and return a validated Config object.

    Args:
        path: Path to the config file. Must exist.
        strict: If True, unknown keys raise ValueError instead of being ignored.

    Returns:
        A validated Config object.

    Raises:
        FileNotFoundError: If `path` does not exist.
        ValueError: If the file is malformed, or contains unknown keys and
            `strict=True`.

    Example:
        >>> config = parse_config(Path("app.toml"))
        >>> config.database.host
        'localhost'
    """
```

**TypeScript / TSDoc:**

```typescript
/**
 * Load a config file from disk and return a validated Config object.
 *
 * @param path - Path to the config file. Must exist.
 * @param strict - If true, unknown keys throw instead of being ignored.
 * @returns The validated Config object.
 * @throws {@link ConfigError} if the file is malformed.
 *
 * @example
 * ```ts
 * const config = await parseConfig('./app.json');
 * ```
 */
```

**Rust / rustdoc:**

```rust
/// Loads a config file from disk and returns a validated `Config`.
///
/// # Arguments
///
/// * `path` - Path to the config file. Must exist.
/// * `strict` - If `true`, unknown keys return `Err` instead of being ignored.
///
/// # Errors
///
/// Returns [`ConfigError::NotFound`] if the file does not exist.
/// Returns [`ConfigError::Malformed`] if parsing fails.
///
/// # Examples
///
/// ```
/// let config = parse_config("app.toml", false)?;
/// ```
```

**Go / godoc** — comment must start with the identifier name:

```go
// ParseConfig loads a config file from disk and returns a validated Config.
// It returns an error if the file does not exist or is malformed.
// If strict is true, unknown keys cause an error instead of being ignored.
func ParseConfig(path string, strict bool) (*Config, error) {
```

### 5. Preserve what's already there

- If a function has a docstring, do NOT overwrite it unless the user asks.
- If a docstring is stale (says the function does something it no longer does),
  flag it to the user rather than silently rewriting.
- Do not change formatting of surrounding code — only add docstrings.

### 6. Show the changes

Print the modified file (or the diffs) and offer to write them to disk.
Do not silently modify files unless the user has said "just do it."

## Anti-patterns

- Do not add docstrings to test cases. Test names should be self-documenting.
- Do not document `self` / `cls` / `this` parameters. Skip them in Args.
- Do not write "This function does..." — start with the verb: "Loads...", "Returns...", "Parses...".
- Do not fabricate types. If a param has no type annotation and the type isn't
  obvious from usage, ask before guessing.
- Do not add docstrings that say the same thing three ways.

