DEV Community

Cover image for Why You Should Stop Reading Tutorials
Deepak Doriya
Deepak Doriya

Posted on

Why You Should Stop Reading Tutorials

What I Learned Auditing a World-Class Python Library (And Why You Should Stop Reading Tutorials)

When you're learning Python, most courses follow the same path: variables, lists, basic loops, object-oriented programming. But there's a massive chasm between a tutorial script and the code that runs production-grade libraries.

To bridge that gap, I recently audited the source of HTTPX — a modern, fully typed HTTP client for Python with 100% test coverage.

Here are 4 advanced patterns I found that standard tutorials never teach you — and how they hold up in real production code.


1. The Naked Asterisk *: Enforcing Clean API Calls

When a function has 10+ optional configurations, it's easy to pass arguments in the wrong order. HTTPX prevents this using a naked asterisk * in its signatures:

def request(self, method: str, url: str, *, headers: dict = None):
    ...
Enter fullscreen mode Exit fullscreen mode

The * acts as a barrier. Every argument placed after it can no longer be passed positionally — the caller is forced to write the parameter name explicitly.

# ❌ Raises TypeError:
client.request("GET", "https://google.com", {"User-Agent": "Bot"})

# ✅ Required syntax (forces clarity):
client.request("GET", "https://google.com", headers={"User-Agent": "Bot"})
Enter fullscreen mode Exit fullscreen mode

Takeaway: Use * to force callers to write explicit, self-documenting code.


2. Sentinels: Solving the "Default Value" Dilemma

We're taught to write timeout = None when a parameter is optional. But what happens if None is actually a valid choice for the user to make?

  • Scenario A: client.get(url) — the user wants the client's default 5-second timeout.
  • Scenario B: client.get(url, timeout=None) — the user explicitly wants no timeout (infinite wait).

If your function signature is timeout=None, you can't tell these two cases apart — omitted and "explicitly None" look identical.

HTTPX solves this with a sentinel object called USE_CLIENT_DEFAULT:

# Define the sentinel
USE_CLIENT_DEFAULT = object()

def get(self, url, timeout=USE_CLIENT_DEFAULT):
    if timeout is USE_CLIENT_DEFAULT:
        timeout = self.default_timeout  # falls back to client default (e.g. 5s)
    # if the user passed None explicitly, we skip the if-block and timeout stays None
Enter fullscreen mode Exit fullscreen mode

Takeaway: Use sentinels when you need to distinguish "argument omitted" from "argument explicitly set to None."


3. Abstract Base Classes (ABCs): Creating Reliable Custom Types

If you want a custom dictionary — like HTTPX's Headers class, which needs case-insensitive keys — your first instinct might be to inherit from dict:

class Headers(dict):
    ...
Enter fullscreen mode Exit fullscreen mode

This is a trap. Built-in types written in C often bypass your overrides. If you override __setitem__ to lowercase keys, methods like dict.update() will ignore your override and write raw keys anyway.

Instead, HTTPX inherits from MutableMapping, an Abstract Base Class:

from collections.abc import MutableMapping

class Headers(MutableMapping):
    ...
Enter fullscreen mode Exit fullscreen mode

By agreeing to this "contract," you only need to implement a handful of core methods (__getitem__, __setitem__, __delitem__, __iter__, __len__). Python then generates all the other dict-like methods (.get(), .pop(), .update()) automatically — and guarantees they route through your custom logic.

Takeaway: Never subclass dict or list for custom containers. Use collections.abc instead.


4. Mypy Strict Mode: Type Safety at Scale

Python is dynamically typed, but large-scale libraries can't afford runtime type errors. HTTPX enforces safety by running mypy in strict mode. In their pyproject.toml:

  • disallow_untyped_defs = true — every function must be fully type-annotated.
  • disallow_incomplete_defs = true — no partially type-hinted signatures.
  • check_untyped_defs = true — mypy still scans untyped functions for logic bugs.

Takeaway: If you're publishing a library, wire up mypy strict mode from day one — retrofitting types onto an untyped codebase later is far more painful.


Conclusion: Stop Reading Tutorials, Start Auditing Code

Instead of reading a standard Python textbook, I decided to do a codebase scavenger hunt challenge on HTTPX — hunting down specific classes, tracing parameters, and figuring out how their types are structured. It forced me to look at actual production code, and it taught me more about intermediate Python design in 30 minutes than any tutorial course could.

If you want to level up, pick a library, set up a few questions to find, and start digging.

Source: httpx/_models.py on GitHub.

Top comments (0)