Documentation is a gift to future-you because it explains intent: why a function exists, what it expects, what it gives back, and what someone should be careful about before they use it.
Good documentation doesn’t repeat every line of code; it names the behaviour that a reader can’t safely infer at a glance.
If tests are executable promises, documentation is the little note tucked beside them saying what you meant and why.
Docstrings
A docstring is a string literal placed at the start of a module, class, method, or function. Python stores it on the object as __doc__, and tools such as help(), pydoc, Sphinx, and editor popups can display it.
PEP 257 is the classic Python docstring convention. It recommends triple double quotes, a short summary line, a blank line before longer detail, and wording that describes what the object does rather than restating its name.
Docstring Formats
You’ll see several common docstring styles, and the important thing is to choose one style for a project and use it consistently.
reStructuredText style is the native language Sphinx understands. It uses field lists such as :param name: and :returns:. It’s powerful, but it can feel visually noisy inside a small function.
Google style uses named sections such as Args:, Returns:, and Raises:. It’s easy to scan in source code, especially when your function already uses Python type hints.
My preference is Google-style Python docstrings. They read well while you’re writing the code, and they can still be published through Sphinx when you enable the napoleon extension.
def build_url_rst(subdomain: str, domain: str, scheme: str="https") ->str:"""Build a URL for a subdomain brute-force attempt. :param subdomain: Candidate subdomain label, such as ``www``. :param domain: Base domain, such as ``example.test``. :param scheme: URL scheme to use. Defaults to ``https``. :returns: A URL string in the form ``scheme://subdomain.domain``. """returnf"{scheme}://{subdomain}.{domain}"def build_url(subdomain: str, domain: str, scheme: str="https") ->str:"""Build a URL for a subdomain brute-force attempt. Args: subdomain: Candidate subdomain label, such as ``www``. domain: Base domain, such as ``example.test``. scheme: URL scheme to use. Defaults to ``https``. Returns: A URL string in the form ``scheme://subdomain.domain``. """returnf"{scheme}://{subdomain}.{domain}"print(build_url("www", "example.test"))
https://www.example.test
Type Hints and Docstrings
Type hints aren’t documentation by themselves, but they reduce how much your docstring has to say. If the function signature already says path: Path and -> int, the docstring can spend its time explaining behaviour, assumptions, and failure modes.
from pathlib import Pathfrom fixtures.local_targets import course_rootdef count_words(path: Path) ->int:"""Return the number of whitespace-separated words in a text file. Args: path: Text file to read. Returns: The number of words found after splitting on whitespace. Raises: FileNotFoundError: If ``path`` doesn't exist. """ text = path.read_text(encoding="utf-8")returnlen(text.split())wordlist = course_root() /"data"/"wordlist.txt"print(count_words(wordlist))
6
Inspecting Documentation
Because docstrings are data, Python can display them while you work. That’s one reason docstrings are more useful than ordinary comments for public functions, classes, and modules.
print(build_url.__doc__.splitlines()[0])
Build a URL for a subdomain brute-force attempt.
Publishing Code Documentation With Sphinx
Sphinx can turn source code, prose, and API reference material into browsable HTML documentation. Teams often publish that HTML as an internal wiki, a static site, or project documentation.
The workflow usually looks like this:
Run sphinx-quickstart to create a docs project.
Write narrative pages for how the tool is meant to be used.
Enable sphinx.ext.autodoc so Sphinx can import your Python modules and pull in docstrings.
Build the site with sphinx-build -b html docs/source docs/build/html.
One practical warning: autodoc imports the modules it documents. Keep import-time side effects out of your tools, and protect command-line entry points with if __name__ == "__main__": so documentation builds don’t accidentally run your program.
Documenting Flask APIs
If you’re building a tool with an HTTP API, hand-written docs are only part of the picture. You also want request and response shapes documented close to the code that handles them.
Packages such as flask-smorest can help document Flask APIs and generate OpenAPI/Swagger documentation from schemas and route metadata. It’s a strong option when you’re already comfortable with Flask blueprints, Marshmallow schemas, and more structured API design.
For a more plain-Flask starting point, Flasgger is worth knowing about. It can attach Swagger UI to a Flask app, read OpenAPI-style details from route docstrings or external YAML files, and give users an interactive /apidocs page without forcing you into a full API framework.
What to Document
For a small security tool, useful documentation usually includes:
what the tool does and what it deliberately doesn’t do;
setup instructions;
safe target assumptions;
examples using documentation-only domains or local fixtures;
command-line options or API endpoints;
common failure modes and how to troubleshoot them;
test instructions, especially anything that avoids touching real client systems.
The goal isn’t to write things down for the sake of writing things down, but to make sure that another user or developer can run, review, and maintain the tool without guessing what you meant. If they have to infer the safety boundary from vibes, the documentation hasn’t done its job.
Checkpoint
Pick one function from your project and write a Google-style docstring for it. Include Args:, Returns:, and Raises: if they apply.
Pick one module or script and add a module docstring that explains the purpose of the file and any safety assumptions.
Draft a short project documentation page for your tool. You can imagine publishing it with Sphinx, an internal wiki, or a GitHub/GitLab wiki. Include setup, safe usage, examples, and testing instructions.
If your project exposes an API, sketch what one endpoint would need in generated Swagger/OpenAPI docs: path, method, parameters, response body, and error cases.
Extension Activity
Generate documentation for one small module with Sphinx or inspect it with help(). Note one thing the generated output explains well and one thing that still needs hand-written prose.
Then ask the slightly rude but useful question: would this have helped you two weeks after writing the code?