About

Updated:

Python folklore outlives the interpreter it was measured on. __slots__ is faster, threads are useless, type hints cost nothing — each of those was true once, each is repeated as a rule today, and none of them arrives with the version it was measured on. The language keeps changing underneath the advice without changing the advice.

Why this site exists

PyDepth is a written record of how Python behaves when you look underneath it. The name is the thesis: Python’s surface is deliberately simple, and almost every performance or correctness surprise comes from something one layer below it. That layer is where an entry starts. It takes a claim that circulates in code review, finds the mechanism that produced it, and measures whether the claim is still true on a current interpreter.

The gap this fills is not a shortage of Python writing. It is a shortage of Python writing that states its interpreter version, prints its command, and pastes its output. An entry succeeds here if a reader can act on it without rerunning the experiment, and can rerun the experiment if they doubt it. Both halves matter — the first is why the numbers sit in the prose, the second is why every harness is published and quoted by path.

The archive is organised into four pillars, and an entry belongs to exactly one of them. Runtime — what CPython does with the objects, modules and frames you hand it: object layout, the import system, the specialising interpreter, memory behaviour. Concurrency — threads, the GIL, asyncio and the event loop, and what each of them actually costs. Tooling — what the toolchain does at build, import or type-check time: import graphs, annotation resolution, packaging. Frameworks — Django, FastAPI and SQLAlchemy inside a real request rather than in the quickstart, counted in queries and milliseconds.

Who writes it

Elliot Sayer writes about Python, its runtimes, frameworks, and the systems behind production applications. His work explores language behavior, concurrency, performance, tooling, and the trade-offs hidden beneath Python’s deceptively simple surface. He prefers source code, reproducible experiments, and measurements over folklore. He writes at PyDepth.

That preference is the whole of his method, and it is why the entries look the way they do. An unattributed number is a rumour, an analogy is how folklore starts, and a conclusion an entry did not demonstrate is the one thing it may not contain. The subjects he returns to are the ones where the mechanism and the folklore have come apart: what an instance dictionary actually shares with its class, what import does to the filesystem before your first line runs, which half of a thread’s work the GIL is holding, what an annotation costs at import time rather than at call time, and where a Django request spends the milliseconds nobody profiles. A framework is measured inside a real request rather than in its quickstart, and a number that will not reproduce on a second run does not become a sentence.

What gets published, and what does not

Every entry answers one question with one of three kinds of evidence, in this order of preference. Source — what the interpreter or the library actually does, read from the code that does it. Reproduction — a script short enough to read, printed in the entry, that produces the behaviour being described. Measurement — a number produced by a harness and printed with its interpreter version and the machine it ran on.

Every entry names the script it ran by path, and those paths resolve against the public experiments repository. Reproducing a figure is a clone and one command. The Django harness wants three, because it needs its own virtual environment before it will run — or one, if you have uv. A measurement is the best of, or the median of, a repeat loop, and the entry says which. Terminal output is pasted, never retyped and never tidied. A figure that cannot be reproduced by running the script behind it does not get published, and where a result is specific to one build or one platform the entry says so rather than generalising.

Four things are deliberately absent. The site does not teach the language: nothing here explains what a decorator is. It does not summarise releases; a release matters here when a measurement changes. It does not run framework comparisons as a genre, and it does not publish anything of the form “X is dead”. And it does not publish an opinion without a measurement behind it. There is no schedule either — an entry ships when its evidence is complete, and the archive is better for the pieces that were abandoned because the number did not survive a second run.

How an entry is made

An entry passes through three steps, in this order, and none of them is skipped.

First, the author picks the subject and writes the draft. The subject comes from something that actually happened in a production system — a claim that kept being repeated in review, a profile that did not match the folklore, a number that moved after an upgrade. The draft is written against a harness that has already been run, so the figures exist before the prose does.

Second, the draft goes through a language model. It strips the identifying detail that a production system carries — service names, hostnames, customer data, anything that would name the system the observation came from — and it fixes spelling, grammar and expression to leave a publishable text. It does not add a claim, and it does not touch a number.

Third, the author reviews the result before anything is published. That pass checks the edited text back against the source and the harness output, restores whatever the edit blunted, fixes what needs fixing, and gives the entry its final form. Nothing reaches the site that the author has not read in the state it will be read in.

Arrow keys to move, Enter to open.