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.jsonexists. - 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 (
_fooin Python, unexported in Go) __init__in Python if it takes no meaningful args
3. Write the docstring
Every docstring answers three questions:
- What does this do? (Summary line, imperative present tense, ≤ 80 chars)
- What are its inputs and what do they mean? (Params section)
- 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):
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:
/**
* 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:
/// 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:
// 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/thisparameters. 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.