Skip to content

BuildNot yet confirmed elsewhere1 publisher2 min readPublished

Python 3.15 lazy imports cut a 512 ms cold start to 32 ms on the route that skips boto3

Python 3.15's PEP 810 lazy imports cut a handler's health-check start from 512 ms to 32 ms in a dev.to benchmark that changed five import lines. The boto3 route fell only from 614 ms to 361 ms, so the gain depends on what a cold process runs first.

The Engineer · Build desk

How we use AISend a correction

Illustration accompanying Python 3.15 lazy imports cut a 512 ms cold start to 32 ms on the route that skips boto3
Generated illustration
Health check drops to 32 ms; boto3 route only to 361 ms Time for the benchmark handler on each route, before and after switching to lazy imports.

Bar comparison of the benchmark handler's times. Health check route: 512 ms with eager imports, 32 ms with lazy imports. The /s3 route, which uses boto3: 614 ms with eager imports, 361 ms with lazy imports.

Health check drops to 32 ms; boto3 route only to 361 ms (Same benchmark handler, eager vs lazy imports, time per route)
ItemValueClaim
Health check, eager imports512 ms5
Health check, lazy imports32 ms5
/s3 route (boto3), eager imports614 ms8
/s3 route (boto3), lazy imports361 ms8

What happened

  • Python 3.15.0 came out on 9 October, and the author compiled it from the python.org source tarball because their usual tooling had no 3.15 builds yet.
  • The test handler imports boto3, httpx, Jinja2, Rich and SQLAlchemy at the top of the file, while its health-check route needs only json.
  • Each case ran 25 times as a fresh process on a 2 vCPU Linux container with a warm bytecode cache, and the post reports the median.
  • Profiling with python -X importtime put boto3 at about 210 ms of cumulative import time and SQLAlchemy at about 155 ms.

Why it matters

  • exposure A missing or misspelled module now fails when production first reaches that code path, so startup stops catching import errors and a CI import smoke test has to do that job.
  • constraint Lazy imports need a 3.15 runtime, and services built on numpy or orjson wait on those projects, since neither had cp315 wheels on PyPI as of 11 October.
  • capability Because __lazy_modules__ keeps plain import statements, teams can ship lazy loading in code that still has to run on 3.14 fleets.
  • cost Upgrading for lazy imports also brings PEP 686's UTF-8 default for open(), a behaviour change on non-UTF-8 locales, mostly Windows, that has to be swept for before the move.

The 16x figure [5] is the startup cost of imports the health route never used. Under eager loading, Python pulled in 551 modules that path never touched before returning its JSON body [6]. Lazy loading took 480 ms off that route [20]. The two heaviest libraries, boto3 and SQLAlchemy, add up to about 365 ms in the post's import profile. That is roughly 71% of the eager 512 ms [22].

The /s3 route is the figure for a request that does real work. It saved 253 ms, a 41% cut or about 1.7x [21]. All of that came from the four libraries the route never loads, and it still pays for boto3 [8]. The author wrote: "Lazy imports move the cost to the first use. They do not delete it." [9]

For the 16x to carry over to a serverless cold start, the first request a fresh process handles has to stay off the heavy imports. Health checks benchmark well because they do almost nothing. We'd expect a function whose cold processes first serve a probe or a light route to land near the 32 ms row. A function whose first request calls S3 should land near the 361 ms row, with boto3 loading on that request [9]. The post timed a container [4]. It did not test a hosted function runtime or report billing.

Beyond the per-import keyword [19], the post documents a global switch. Setting `python -X lazy_imports=all` or `PYTHON_LAZY_IMPORTS=all` makes every import lazy, and `sys.get_lazy_imports()` then returns "all" [10]. Pairing that with `sys.set_lazy_imports_filter()` keeps chosen modules eager: the filter is consulted only for imports that would be lazy, and a False return holds the module eager [13]. The modules to hold back are the ones whose import does work. Plugin registration, model registries, monkeypatching and logging configuration done at import time will not run until something touches the module [15]. Of the global mode, the author wrote: "Good for an experiment in staging, risky as a default on day one." [11] Two forms cannot be lazy at all. `lazy from x import *` and `lazy from __future__ import ...` are syntax errors [16].

In our view the keyword is the right place to start. Each lazy line is explicit and shows up in code review [19]. On this handler, two of the five lines cover most of the measured import time [22]. Whether the work justifies its own budget line depends on how often a cold process's first request lands on a light route.

What to watch

  • cp315 wheels for numpy and orjson appearing on PyPI, which decides when data-heavy services can adopt the change.
  • A cold-start measurement on a hosted serverless runtime, where the type of the first request decides which modules load.
  • Libraries that register plugins or configure logging at import time documenting whether they must stay eager under PYTHON_LAZY_IMPORTS=all.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence45
Adoption
Insufficient
Hype gap+20
Incentives30
Confidence45
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    Python 3.15.0 came out on 9 October; with no 3.15 builds available through the author's usual tooling, the author built it from the python.org source tarball, SHA-256 checked against the release page.

    ReportedSupportedSource: dev.to post on Python 3.15 lazy importsView cited source
  2. [2]

    The test handler imports boto3, httpx, Jinja2, Rich and SQLAlchemy at the top, but its health check path only needs json.

    ReportedSupportedSource: dev.to post, author's benchmark handlerView cited source
  3. [3]

    The lazy version of the handler changes five import lines and nothing else.

    ReportedSupportedSource: dev.to postView cited source

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · October 11, 2026

    Python 3.15 lazy imports took my cold start from 512 ms to 32 ms

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Loading related stories