30 Async Rules
Thirty rules for writing correct, performant asyncio code in Python 3.14. Violating these causes subtle bugs that only appear under load.
Search across all documentation pages
Thirty rules for writing correct, performant asyncio code in Python 3.14. Violating these causes subtle bugs that only appear under load.
Review when adding
async defto any module. Pair with pytest-asyncio tests.
When to reach for this:
asyncio.run() inside a running loop.asyncio.run() as main entry - top-level script and sync test runner only.asyncio.TaskGroup (3.14) - structured concurrency over bare create_task.RuntimeWarning and do nothing.requests in async def blocks the loop.asyncio.to_thread() for blocking I/O - when no async library exists.asyncio_mode = "auto" in pyproject.toml.asyncio.CancelledError gracefully.asyncio.wait_for() or httpx timeout=.ProcessPoolExecutor.async with sem: limits parallel requests.asyncio.gather for independent tasks - return_exceptions=True for partial failure.asyncio.Queue for producer-consumer - bounded queues prevent memory blowup.AsyncClient or pool per app, not per request.lifespan or @app.on_event startup.async with session.begin(): for transaction boundaries.StreamingResponse instead of loading full body in memory.time.sleep() in async - use await asyncio.sleep().@lru_cache on async functions - cache coroutine objects, not results. Use async_lru.aiodns or run resolver in thread for high-concurrency apps.task.add_done_callback to log unhandled errors.await asyncio.to_thread(blocking_fn).return_exceptions=True or explicit try/except per task.| Alternative | Use When | Don't Use When |
|---|---|---|
| threading | Blocking libraries only | High concurrency I/O |
| multiprocessing | CPU-bound parallelism | Shared state needed |
| Sync FastAPI + workers | Team unfamiliar with async | I/O-heavy with async drivers available |
When the app is I/O-bound and async libraries exist for your database, HTTP, and cache.
No. Sync routes work via thread pool. Async routes preferred with async DB drivers.
TaskGroup (3.11+) for structured concurrency with automatic cancellation on failure.
py-spy on the process. Look for sync blocking in async call stacks.
async with async_session() as session: per request via FastAPI dependency.
Django 5.x supports async views. ORM async support is partial; evaluate per use case.
Start with semaphore of 10-50. Tune based on connection limits and memory.
anyio bridges asyncio and trio. FastAPI ecosystem is asyncio-native.
async for with async generators. Close with aclose() in finally blocks.
One endpoint at a time. Swap sync driver for async driver. Test under load.
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026