Formatting, Linting, and Documentation

Updated

September 8, 2026

Formatting, Linting, and Documentation

ruff format is not optional. You do not argue about spaces around =. You run the formatter. ruff check catches unused names and a handful of real mistakes. A docstring is a """triple-quoted string""" right under a def, and help() reads it.

Mental model

Three layers, in this order:

  1. Format — how the file looks (ruff format). Mechanical. No taste.
  2. Lint — cheap static checks (ruff check). Unused imports, unused variables, a few footguns.
  3. Docs — a sentence for a function another person (or you, in six months) will call. Not a restatement of the name.

help(fn) prints the function’s signature and docstring. It works on your functions and on the built-ins.

Add the tool once:

uv add --dev ruff

Worked examples

Case 1: Format is non-negotiable

Save as messy.py. It runs. It looks like a fight.

# messy.py
def main( ):
    tables=8
    print( f"tables {tables}" )


if __name__=="__main__":
    main()

Run:

uv run ruff format messy.py
uv run python messy.py

Formatter output:

1 file reformatted

Program output:

tables 8

The file on disk is now:

# messy.py
def main():
    tables = 8
    print(f"tables {tables}")


if __name__ == "__main__":
    main()

Do not hand-match this style. Run ruff format. In a project, uv run ruff format . formats every Python file.

Case 2: A lint finding, then the fix

Save as lint_bug.py. The unused import is the bug ruff check is for.

# lint_bug.py
import json


def main():
    print("shift open")


if __name__ == "__main__":
    main()

Run:

uv run python lint_bug.py
uv run ruff check lint_bug.py

The program still prints:

shift open

The linter reports:

F401 [*] `json` imported but unused
 --> lint_bug.py:2:8
  |
1 | # lint_bug.py
2 | import json
  |        ^^^^
help: Remove unused import: `json`

Found 1 error.
[*] 1 fixable with the `--fix` option.

Fix — save this as lint_bug.py (or run uv run ruff check --fix lint_bug.py):

# lint_bug.py
def main():
    print("shift open")


if __name__ == "__main__":
    main()
uv run ruff check lint_bug.py
uv run python lint_bug.py

Linter:

All checks passed!

Program:

shift open

F401 is not a taste note. An unused import is a lie about what the file needs. Delete it.

Case 3: A docstring help() can read

Save as ticket_help.py. The first statement in the function is a string. That string is the docstring.

# ticket_help.py
def label(ticket_id, table):
    """Return a one-line label for a ticket at a table."""
    return f"ticket {ticket_id} → table {table}"


def main():
    help(label)
    print(label(7, 12))


if __name__ == "__main__":
    main()

Run:

uv run python ticket_help.py

Output:

Help on function label in module __main__:

label(ticket_id, table)
    Return a one-line label for a ticket at a table.

ticket 7 → table 12

help(label) reads label.__doc__. You did not write a website. You wrote the sentence that shows up when someone asks the runtime.

Case 4: help() on a built-in

Save as help_len.py.

# help_len.py
def main():
    help(len)


if __name__ == "__main__":
    main()

Run:

uv run python help_len.py

Output (first lines):

Help on built-in function len in module builtins:

len(obj, /)
    Return the number of items in a container.

When you forget an argument, help is faster than a search tab.

The trap

Turning the formatter off because you “like tabs,” or sprinkling # noqa until ruff check is quiet. The team then has two styles and no signal.

The docstring trap is the opposite of silence: a paragraph that repeats the name.

# obvious.py
def add(a, b):
    """Add a and b and return the sum."""
    return a + b


def main():
    print(add(2, 6))


if __name__ == "__main__":
    main()

Run:

uv run python obvious.py

Output:

8

It works. The docstring adds nothing. Write a docstring when the function hides a rule (prices cannot be negative, status is open|paid). Skip it when the name and the signature are the documentation.

The boring rule

  • Run uv run ruff format . before you commit. No debate.
  • Run uv run ruff check .. Fix F401 and friends. Do not blanket-ignore.
  • Docstrings use """triple quotes""" on the line after def.
  • One sentence is enough for most functions. help(fn) should be readable.
  • Do not document add. Do document line_total if it rejects negative prices.

Try this

  1. Break spacing in ticket_help.py on purpose. Run uv run ruff format ticket_help.py and confirm it snaps back.
  2. In lint_bug.py, add import sys and do not use it. Confirm ruff check reports F401 again, then delete the import.
  3. Give label a second sentence in the docstring that says the arrow is literal text. Run ticket_help.py and read help() again.