Python 3.15.0b4 is the final planned beta, but the official release page still says it is not recommended for production. The useful action now is to add an isolated CI lane that exposes dependency, encoding, import-side-effect, C-extension, and free-threading problems without changing the production runtime.
What stage is Python 3.15 in?
The official Python 3.15.0b4 release page dates the release to July 18, 2026 and calls it the final planned beta. It also describes 3.15 as a preview release and explicitly advises against production use. The project intends to avoid ABI changes after beta 4, but that goal is not a production-stability guarantee.
PEP 790 schedules the first release candidate for August 4 and the final release for October 1, 2026. Treat each environment differently:
| Environment | Recommended action now |
|---|---|
| Local development | Create an isolated 3.15 environment for compatibility work |
| CI | Add a prerelease lane, initially allowed to fail with tracked issues |
| Staging | Exercise the real dependency lock and traffic model |
| Production | Remain on a supported stable version until final and ecosystem readiness |
This staged gate follows the same rule as revision-safe bilingual publishing: tests passing, an image building, a deployment completing, and public service health are different states.
Step 1: add a prerelease lane without changing the default
Start from the stable branch and add 3.15 as a separate matrix entry:
strategy:
matrix:
python-version: ["3.12", "3.13", "3.14", "3.15"]
fail-fast: false
steps:
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- run: python -m pip install -U pip
- run: python -m pip install -r requirements.txt
- run: python -X dev -W error::DeprecationWarning -m pytest
It is reasonable for the prerelease lane to use continue-on-error at first, but every failure needs an owner or upstream link. “Allowed to fail” must be a temporary discovery state, not a permanent blind spot. Promote the lane to required after critical dependencies and project tests are ready.
Step 2: audit dependencies before blaming application code
Many early compatibility failures come from missing binary wheels rather than Python syntax. Classify each failure:
- No 3.15 wheel exists, but a source build works.
- The build backend does not recognize the new interpreter or wheel tag.
- A C, C++, or Rust extension uses changed or removed C APIs.
- A package depends on import-time global side effects.
- A package claims interpreter support but has not tested free-threaded mode.
Run two installation policies to distinguish distribution gaps from source incompatibility:
python -m pip install --only-binary=:all: -r requirements.txt
python -m pip check
python -c "import ssl, sqlite3, multiprocessing"
Then allow source builds in a separate job. These commands are a validation template, not evidence that a particular application already supports 3.15.
Step 3: make encoding contracts explicit
Python 3.15 uses UTF-8 as the default encoding. That reduces platform variance, but legacy code that intentionally or accidentally relied on a locale encoding can silently change behavior. Search for file operations without an explicit encoding=:
rg "open\\(|Path\\(.*\\)\\.(read_text|write_text)" .
State the contract for configuration, exchange formats, and generated files:
from pathlib import Path
config = Path("config.json").read_text(encoding="utf-8")
Path("report.txt").write_text(report, encoding="utf-8", newline="\n")
Test non-ASCII paths, Chinese content, BOM-prefixed inputs, invalid byte sequences, and Windows newlines. A UTF-8 default does not mean every user upload or external protocol is UTF-8; those boundaries still need validation and clear error behavior.
Step 4: treat explicit lazy imports as a semantic test
The Python 3.15 What's New documentation lists explicit lazy imports among the major features. Faster startup is valuable, but hidden import side effects make activation order harder to reason about. Search for modules that:
- Register plugins, routes, or signals during import.
- Perform environment checks, network calls, or logging setup at import time.
- Depend on import order to initialize a global singleton.
- Pass tests only when module A happens to load before module B.
Move these effects into explicit create_app(), register_plugins(), or lifecycle hooks. FastAPI projects should not hide database connections, queue consumers, or large model loads in module top level. The clear routing boundaries described in ZoyTown's dynamic SSR guide are equally useful for runtime initialization.
Step 5: decide whether abi3t is relevant to your team
Most application teams should not modify ABI settings directly. The official abi3t migration guide is aimed at maintainers of direct C or C++ extensions. Python 3.15 introduces abi3t, a Stable ABI variant for free-threaded builds. It can reduce the future wheel matrix, but it limits available C APIs and may introduce performance tradeoffs.
| Team type | Practical decision |
|---|---|
| Pure Python application | Validate dependency wheels; do not invent ABI work |
| Application consuming C extensions | Track upstream 3.15 and free-threaded support |
| Handwritten C/C++ extension | Prove thread safety, then evaluate abi3 and abi3t |
| Cython, PyO3, or other generator user | Wait for explicit toolchain support |
“Free-threaded Python is supported” does not imply that your entire dependency graph is thread-safe. Test shared mutable state, caches, reference lifetimes, callback order, and lock ordering. A single-threaded suite cannot prove concurrent safety.
Step 6: create separate performance baselines
An interpreter upgrade can change startup, import behavior, JIT behavior, and extension code at the same time. Do not attribute an end-to-end result to one feature. Compare 3.14 and 3.15 with the same dependency lock, container limits, dataset, and traffic model:
- Cold start: process launch to readiness.
- Hot requests: throughput and p50/p95/p99 latency.
- Memory: idle RSS and steady-load RSS.
- Background work: queue duration and cancellation latency.
- Failure behavior: timeouts, pool exhaustion, and 5xx rate.
Published interpreter benchmarks are useful for forming hypotheses, not for predicting a specific FastAPI service. Profile the actual workload and state whether a number is measured, simulated, or only an example.
Step 7: design a reversible rollout gate
After the final release, avoid an all-at-once runtime switch:
- Build a 3.15 image and generate an SBOM.
- Lock dependency hashes and verify wheel provenance.
- Run unit, integration, migration, recovery, and load tests.
- Send a small canary share and compare errors, latency, and memory.
- Keep the previous stable image and a data-compatible rollback path.
- Increase exposure only while predefined thresholds hold.
If new code writes a different serialized form, rolling back the interpreter does not roll back data. Database schemas, queue messages, and cache versions need backward compatibility, or an expand-then-contract migration.
Common mistakes
“No ABI changes are planned after beta 4, so production is safe”
The same official release page that states the ABI goal says the beta is not recommended for production. ABI stability does not cover every standard-library bug, dependency issue, or application semantic change.
“The requirements installed, so the application is compatible”
Installation proves resolution and build only. Test imports, startup, requests, workers, serialization, signals, subprocesses, and graceful shutdown.
“Free-threaded mode will automatically speed up FastAPI”
Results depend on the bottleneck, dependency safety, and concurrency design. An I/O service may be dominated by databases, networks, and connection pools. CPU-heavy code still needs workload-specific profiling.
Which runtime boundaries are easy to miss?
Do not stop at the HTTP happy path. Build a subsystem matrix:
| Subsystem | Behaviors to test |
|---|---|
asyncio |
Cancellation, timeout, TaskGroup propagation, shutdown |
multiprocessing |
Start method, pickle boundaries, worker recycling |
| TLS and HTTP | Certificate validation, proxies, reuse, timeout |
| Data layer | Driver import, pools, BSON/JSON, transaction retries |
| CLI | Locale, stdin/stdout encoding, exit codes |
| Observability | Logging, trace context, and profiler integration |
Run with -X dev and warnings as errors, then run again with production-like flags so the diagnostic mode does not hide a timing difference. For scheduled jobs and workers, test SIGTERM handling, lease release, and duplicate delivery rather than checking only the web process.
What additional gates do library maintainers need?
Library teams must separate source compatibility from distribution compatibility:
- Set
python_requiresfrom evidence, not an optimistic declaration. - Build the sdist in a clean environment.
- Ensure regular and free-threaded wheel tags reflect actual support.
- Import-test artifacts on macOS, Windows, and manylinux targets.
- Run type checking, documentation builds, and public examples against the same API.
If you publish prerelease wheels, use an explicit prerelease version and release notes so normal users do not install them accidentally. The Python release page encourages maintainers to create test wheels while recommending that ordinary production releases wait until rc1.
FAQ
Should a project raise its minimum version to 3.15 now?
Usually no. Beta testing is for finding compatibility problems, not immediately dropping users on supported interpreters. The minimum version follows product lifecycle, dependency, and security-support decisions.
Can you switch interpreters inside one virtual environment?
Do not. Environment paths, ABI, and installed wheels are interpreter-specific. Create a new 3.15 environment and reinstall the locked dependencies.
Will lazy imports silently change all code?
Use the current 3.15 documentation and actual interpreter flags rather than inferring behavior from a feature name. Removing import side effects is still valuable because explicit lazy imports—or dependencies that adopt them—make those hidden assumptions visible.
Migration checklist
- [ ] An isolated 3.15 CI lane exists and failures are tracked.
- [ ] Wheel-only, source-build, and
pip checkresults are separated. - [ ] File and protocol encodings are explicit.
- [ ] Import side effects live in explicit lifecycle functions.
- [ ] C-extension and free-threaded support is recorded per dependency.
- [ ] 3.14 and 3.15 use the same performance baseline.
- [ ] Canary thresholds, rollback image, and data compatibility are defined.
- [ ] Production review waits for final and critical dependency readiness.
Report the outcome as evidence: which tests ran, which packages remain blocked, whether only the build succeeded, whether a canary ran, and whether the rollout reached full traffic. The same source discipline is explained in ZoyTown's official-source GEO/AEO guide.