Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How can a function docstring explain its contract without repeating its code?

Quick answer

Describe what callers need to know, not the steps the function takes. State the function’s purpose, any important input requirements, what it returns, and errors callers may need to handle. Skip details that are obvious from the signature or implementation. A useful test: could someone use the function correctly without reading its body?

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

How do I tell whether a docstring is repeating the code instead of explaining the function?

Geez

A docstring repeats the code when it narrates operations a reader can already see, such as “checks the start, adds the size, then slices the list.” A contract describes the behavior callers rely on: what the function is for, what inputs it accepts, what comes back, and what errors can occur.

A quick check is to hide the implementation and read the docstring alongside the function signature. If a caller still cannot tell how to use the function or what result to expect, add the missing contract detail. If a sentence merely translates one line of code into English, remove it.

Follow-up question

Can you show the difference with a small example?

Geez

Suppose a function returns a portion of a list. Here’s a before-and-after comparison. The first docstring narrates implementation steps:

Before:

"""Check start and size, add them, and return a slice."""

That leaves a caller guessing about valid values and the result. The revised docstring explains the contract:

After:

def page(items, start, size):
    """Return up to size items from items, starting at start.

    Args:
        items: The list to read from; it is not modified.
        start: Zero-based starting index; must be nonnegative.
        size: Maximum number of items; must be at least 1.

    Returns:
        A new list containing the selected items.

    Raises:
        ValueError: If start is negative or size is less than 1.
    """
    if start < 0 or size < 1:
        raise ValueError("start must be nonnegative and size must be at least 1")
    return list(items[start:start + size])

The wording states guarantees a caller may depend on: the starting index is zero-based, the result is a new list, and invalid bounds raise ValueError. It does not explain the addition or slicing because those steps are visible in the body. This example assumes items is a list and that the function is intended to return a list; adjust the contract if your function behaves differently.

Follow-up question

What belongs in the argument and error sections? Should I document every parameter and every possible problem?

Geez

Include a parameter when its meaning, accepted values, or effect is not already clear from its name and type. In the example, start needs the zero-based detail, and size needs its minimum; simply repeating “start is an integer” may add little if the signature already makes that clear.

Document errors when callers can reasonably encounter them and may need to respond—for example, the deliberate ValueError for invalid bounds. Don’t list hypothetical failures the function does not explicitly define. If a parameter has a default, affects mutation, or changes the result in a non-obvious way, explain that behavior when it matters to callers.

Follow-up question

What’s a practical way to review my docstring before sharing the function?

Geez

Read it as a caller who has only the function name, signature, and docstring. Check that the reader can decide what to pass, predict the result, and understand any important failure or side effect. Then compare each sentence with the function body: keep statements about externally visible behavior; cut narration of internal steps.

For the page example, a caller can tell that start counts from zero, size is a maximum, the input list is left alone, and invalid bounds raise an error. The docstring does not promise behavior beyond those stated assumptions. If the implementation later changes but the contract remains true, the docstring should not need to change.

A practical checklist

  • State the function’s purpose in one short opening sentence.

  • Explain input constraints or meanings that are not clear from the signature.

  • Describe the return value, including important limits or shape.

  • Mention errors callers can encounter and may need to handle.

  • Note meaningful mutation or side effects.

  • Remove sentences that only narrate visible implementation steps.

A few more useful answers

Should every argument appear in the docstring?

Not necessarily. Document arguments when their meaning, constraints, or effect needs clarification; avoid restating information already obvious from a clear signature.

Do I need to describe how the function works internally?

Usually not in a caller-facing contract. Explain internal design only when it affects how someone should use, maintain, or rely on the function.

Should I list every error that might happen?

Focus on errors the function deliberately raises or that callers need to understand. Avoid implying that an error is part of the contract if the function does not define that behavior.

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work