docstring-and-inline-docs

verified

8398e0a7-b8d3-4185-9d51-9bbac85522c2

Write docstrings and comments that explain *why* and *contract*, not what the code visibly does. Use when adding public functions or clarifying non-obvious logic.

Metadata

Skill ID
8398e0a7-b8d3-4185-9d51-9bbac85522c2
Version
1
Owner
387274b7-2891-478b-81b8-e11d5adb9319
Tags
docstringcommentsdocumentationwhy-not-whatcode-docs
Signature
verified
Integrity
OK
Content hash
b6de0db5f4eca9a2a03ff9441e2bad8ebb50976d90715525d26ff1bfceefcfcd
Created
2026-08-15T05:24:22Z

Skill file

Raw skill file (markdown source)
# Docstrings and Inline Documentation

Use when writing or reviewing function/method documentation — the goal is to document the contract (what, params, return, raises) and the intent (why), not to restate the code.

## The Docstring Contract

### Python (Google-style)
```python
def transfer_funds(
    from_account: str,
    to_account: str,
    amount_cents: int,
    *,
    idempotency_key: str | None = None,
) -> TransferResult:
    """Move money between two accounts atomically.

    Both accounts must belong to the same currency. This is an idempotent
    operation — passing the same idempotency_key twice returns the original
    result without double-charging.

    Args:
        from_account: Source account ID (e.g., "acct_abc123").
        to_account: Destination account ID. Must differ from from_account.
        amount_cents: Amount to transfer in cents (USD). Must be positive.
        idempotency_key: Optional client-generated key for safe retries.

    Returns:
        TransferResult with transfer_id, status, and ledger entries.

    Raises:
        InsufficientFundsError: from_account balance < amount_cents.
        CurrencyMismatchError: Accounts have different currencies.
        ValueError: from_account == to_account, or amount_cents <= 0.
    """
```

### JavaScript/TypeScript (JSDoc)
```typescript
/**
 * Move money between two accounts atomically.
 *
 * @param fromAccount - Source account ID.
 * @param toAccount - Destination account ID. Must differ from source.
 * @param amountCents - Transfer amount in cents (must be positive).
 * @param idempotencyKey - Optional key for safe retries.
 * @returns Transfer result with ID, status, and entries.
 * @throws {InsufficientFundsError} If source balance is insufficient.
 */
async function transferFunds(
  fromAccount: string,
  toAccount: string,
  amountCents: number,
  idempotencyKey?: string
): Promise<TransferResult> { ... }
```

### Go
```go
// TransferFunds moves money between two accounts atomically.
//
// Both accounts must share the same currency. Passing the same idempotencyKey
// twice returns the original result without double-charging.
//
// Errors:
//   - ErrInsufficientFunds if the source account balance is too low.
//   - ErrCurrencyMismatch if the accounts have different currencies.
func TransferFunds(
    fromAccount, toAccount string,
    amountCents int64,
    idempotencyKey *string,
) (TransferResult, error) { ... }
```

## The Comment Rule: Why, Not What

```python
# ❌ BAD — restates the code
# Loop over items and add to total
for item in items:
    total += item.price

# ✅ GOOD — explains why this is done this way
# Accumulate in cents (int) to avoid floating-point rounding.
# Conversion to dollars happens only at display time.
for item in items:
    total += item.price_cents

# ❌ BAD — obvious from the code
i += 1  # increment i

# ✅ GOOD — explains a non-obvious constraint
i += 1  # Skip the header row (index 0) which contains column names

# ✅ GOOD — explains a workaround
# SQLAlchemy 2.0.23 has a bug where joinedload with limit() generates
# a cartesian product. Use selectinload instead until the fix ships.
# See: https://github.com/sqlalchemy/sqlalchemy/issues/12345
```

## When to Comment vs Refactor

| Situation | Action |
|---|---|
| The code is hard to understand | Refactor first, then comment any remaining non-obvious choices |
| There's a business rule or domain constraint | Comment the rule and its source (ticket, spec, regulation) |
| A workaround for a library bug | Comment with the bug link and version affected |
| A performance optimization that reads oddly | Comment with the before/after benchmark |
| Standard, idiomatic code | No comment needed |
| A TODO or FIXME | Either do it now, or file a ticket and reference it |

## Removing Stale Comments

```bash
# Find comments that may be lying — look for function signatures that changed
git log --all -p -S "old_param_name" -- '*.py'

# Find TODO comments older than 6 months
git log --all --since="6 months ago" --grep="TODO" --oneline
```

## Guardrails

- **Never** commit commented-out code. Delete it. Git history preserves it.
- **Never** write docstrings that repeat the function signature — the types + name tell you "what"; the docstring tells you "why" and "what can go wrong."
- **Never** leave stale comments that contradict the code. A wrong comment is worse than no comment.
- **Always** document: side effects (writes to DB, sends email, modifies global state), thread-safety guarantees, and performance characteristics for large inputs.

## Pitfalls

- **Comments that drift from code**: Parameter renamed from `user` to `account` but the comment still says "the user object." Use a linter to flag mismatches.
- **Docstrings that restate the signature verbatim**: `def foo(x: int) -> str: """Foo takes x and returns a string."""` — this adds zero information. Delete it or explain the contract.
- **Over-commenting clean code**: If every line has a comment, the code is either too complex or the comments are noise. Refactor instead.
- **TODO as a graveyard**: "TODO: handle errors" from 2022. TODOs without ticket links rot. File an issue and reference it, or fix it now.
- **Forgetting to update docstrings when changing behavior**: Added a new `raises` condition? Update the docstring. Changed the return format? Update the docstring.

## Verify / Checklist

- [ ] Every public function/class has a docstring with purpose, params, return, and raises
- [ ] No comment restates what the code visibly does; every comment explains why or documents a constraint
- [ ] No commented-out code remains
- [ ] All TODO comments link to a ticket or issue
- [ ] Docstrings match current function signatures (run `pydocstyle` or `darglint`)
- [ ] Function parameter names in docstrings match actual parameter names
- [ ] Side effects and thread-safety notes are documented where applicable

Attached files

No attached files.