FastAPI has become one of the default choices for modern Python APIs.
It is clean, fast to build with, developer-friendly, and works extremely well for many production systems. But there is a trap that shows up as teams scale:
People think “async” automatically means “fast.”
It does not.
Async is a concurrency model. It is not magic. It does not fix slow database queries, blocking SDKs, overloaded thread pools, poor connection pooling, bad deployment settings, missing observability, or external APIs taking 3 seconds to respond because the internet remains a deeply unserious place.
This week’s edition is about what production FastAPI experience really means, how Python engineers can show it, and how hiring managers can test for it.
The signal this week
The most useful Python discussion this week was around whether teams are running FastAPI in production under heavy traffic.
The interesting part was not “does FastAPI scale?”
The better question was:
“Do the engineers understand what they are scaling?”
A FastAPI app can perform extremely well. But performance depends on the full request path:
ASGI server setup
worker processes
event-loop health
blocking vs non-blocking work
AnyIO thread pool behaviour
database connection pooling
external API latency
background work
p50, p95, and p99 latency
memory and CPU usage
deployment strategy
observability
That is a lot more useful than simply asking whether someone has used FastAPI.
Async is not a performance sticker
A common misunderstanding:
“If I use async def, my endpoint is fast.”
Not necessarily.
An async def endpoint is only useful if the work inside it cooperates with the event loop.
If your endpoint calls blocking code, like a synchronous database driver, synchronous HTTP client, file operation, slow third-party SDK, or CPU-heavy function, you can still block progress.
That means other requests may wait.
That means tail latency increases.
That means the team starts blaming FastAPI when the real issue is a blocking call wearing a trench coat.
A better mental model:
async defis good for non-blocking I/Onormal
defendpoints are run in a thread poolblocking work needs to be isolated properly
CPU-heavy work may need worker processes, queues, or a separate service
the database often matters more than the framework
external services can dominate request time
The framework is rarely the whole story.
What happens with def vs async def
FastAPI behaves differently depending on how you define a path operation.
A normal def route is run in an external thread pool so it does not block the event loop directly.
An async def route runs on the event loop and should avoid blocking operations.
That distinction matters.
A team that does not understand it can accidentally create performance issues while thinking they are being modern.
Example problem:
@app.get("/reports")
async def get_report():
data = requests.get("https://slow-api.example.com/report")
return data.json()
This looks async because the function is declared with async def.
But requests.get() is synchronous and blocking.
Better:
@app.get("/reports")
async def get_report():
async with httpx.AsyncClient(timeout=5) as client:
response = await client.get("https://slow-api.example.com/report")
return response.json()
Even then, you still need timeouts, retries, rate limits, error handling, and monitoring.
Congratulations, we discovered production.
When a FastAPI service slows down, people often look at the framework first.
They should usually look at the system.
Common bottlenecks:
1) Blocking I/O inside async routes
Synchronous HTTP clients, file reads, slow SDKs, blocking database drivers, or anything that does not yield control properly.
2) Database pool waits
If your API can handle many concurrent requests but your database pool is tiny or exhausted, the bottleneck becomes waiting for connections.
3) Slow queries
A badly indexed query does not care that your route is async. It will ruin your day with database-shaped confidence.
4) Thread pool saturation
If too much sync work is pushed into a thread pool, it can become a bottleneck.
5) External API latency
A 50ms FastAPI route that calls a 3-second external API is a 3-second endpoint wearing a nice jacket.
6) Background work inside request lifecycle
Heavy work like OCR, inference, PDF processing, email sending, or large exports should usually not sit inside the user-facing request path.
7) Too few or too many workers
Worker count affects throughput, memory, CPU use, and deployment behaviour. More workers are not always better.
8) Poor observability
If you cannot see p95 latency, database wait time, event-loop lag, failed background tasks, and external service latency, you are guessing.
Guessing is cheaper than observability until it becomes very expensive.
A production FastAPI checklist
If you are building or reviewing a FastAPI service, this is the checklist I would use.
Request path
Are routes thin?
Is business logic outside the handler?
Are blocking calls isolated?
Are timeouts set on external calls?
Are errors mapped consistently?
Is payload size controlled?
Async and concurrency
Do
async defroutes avoid blocking operations?Are sync routes intentionally sync?
Are long-running jobs moved out of request lifecycle?
Are CPU-heavy tasks isolated?
Is thread pool usage understood?
Database
Is the DB driver async or sync?
Is connection pooling configured?
Are sessions scoped safely?
Are transactions explicit?
Are slow queries monitored?
Are indexes reviewed?
Deployment
How many workers are used?
Is memory monitored per worker?
How are restarts handled?
Is the app deployed in containers?
Does the orchestration layer handle replication?
Are health checks meaningful?
Observability
p50, p95, and p99 latency
event-loop lag
database connection waits
external API latency
thread pool saturation
request volume
error rate
memory and CPU per worker
background task failures
This is what “production FastAPI” actually means.
Not just “the docs page loads.”
Candidate takeaway: how to talk about FastAPI properly
If you are a Python engineer interviewing for backend roles, do not just say:
“I’ve built APIs with FastAPI.”
Say something like:
“I’ve used FastAPI in production and I’m careful about the difference between async and blocking work. I avoid synchronous calls inside async endpoints, use timeouts on external services, monitor p95 latency, check database pool waits, and move long-running work outside the request lifecycle.”
That is a much stronger signal.
You can also build a project that proves this.
A strong portfolio project:
FastAPI
Postgres
SQLAlchemy or SQLModel
async database sessions
HTTPX for external API calls
timeouts and retry strategy
background worker for long-running tasks
structured logging
Prometheus or OpenTelemetry metrics
Docker
load testing with Locust or k6
README explaining performance decisions
Add a README section called:
“How this behaves under load”
Include:
what happens if the external API is slow
what happens if the database pool is exhausted
what gets moved to background jobs
what metrics you track
how you tested p95 latency
what you would improve next
That turns a basic API into a production-minded project.
Hiring manager takeaway: what to ask instead
If you are hiring Python engineers, especially senior or lead-level, ask practical production questions.
Good questions:
“What is the difference between
defandasync defin FastAPI?”“What happens if you call
requests.get()inside an async endpoint?”“How would you investigate high p95 latency?”
“How do you choose worker count?”
“How do you configure database connection pools?”
“When would you move work out of the request lifecycle?”
“How do you monitor event-loop lag or thread pool saturation?”
“What are the limits of FastAPI’s
BackgroundTasks?”“How do you prevent external APIs from slowing down your whole service?”
“What would you load test before launch?”
These questions separate people who have used FastAPI from people who understand backend systems.
A useful interview task
Give the candidate a small FastAPI endpoint like this:
@app.post("/generate-report")
async def generate_report(request: ReportRequest):
customer = db.query(Customer).filter(Customer.id == request.customer_id).first()
response = requests.post("https://external-api.example.com/report", json=request.model_dump())
pdf = build_large_pdf(response.json())
send_email(customer.email, pdf)
return {"status": "sent"}
Ask:
“What is wrong with this in production, and how would you restructure it?”
A strong candidate should mention:
sync database access inside async route
blocking HTTP call
missing timeout
heavy PDF generation in request path
email sending inside request path
missing retry strategy
missing idempotency
missing error handling
no observability
potential DB session problems
need for background workers or task queue
endpoint should return job status rather than wait for everything
That one exercise tells you more than 20 minutes of framework trivia.
Why this matters for AI startups
AI product companies often build FastAPI services around:
model calls
external AI providers
document processing
embeddings
retrieval
agent workflows
long-running jobs
streaming responses
user-facing APIs
That makes async and production behaviour even more important.
A slow external model call can dominate latency.
A background job failure can break the user experience.
A missing timeout can jam the request path.
A badly managed DB session can turn load into chaos.
AI does not remove backend fundamentals. It makes them more important.
Nature is healing, and by “nature” I mean production engineering continues to punish optimism.
Quick Python watch
A few useful updates from the last 7 days:
Python 3.15 release candidate window
Python 3.15.0rc1 was scheduled for 4 August, which means maintainers should now be testing packages, CI, wheels, and internal tooling against 3.15 rather than waiting until final release.
Python 3.14.7 and 3.13.15
Python 3.14.7 and 3.13.15 were announced this week. Maintenance releases matter because they can include bug fixes and security backports that reduce the need for rushed minor-version upgrades.
FastAPI 0.140.1 and 0.140.2
FastAPI 0.140.1 and 0.140.2 landed on 27 July. If your team is moving quickly on FastAPI versions, pin and upgrade intentionally.
Ruff 0.16.0
Ruff 0.16.0 shipped with a much larger default ruleset, which is a reminder to pin tooling deliberately and avoid surprise CI failures.
uv 0.11.33
uv 0.11.33 was released on 28 July, continuing the broader trend of Python packaging and workflow tooling moving quickly.
Job of the week
Lead Software Engineers
AI x Electronics | Series A | London hybrid | up to £130k base + equity
A London-based Series A startup building AI for electronics is hiring multiple Lead Software Engineers.
They are building software that helps turn hardware architecture into circuit board schematics dramatically faster, applying AI to a technical domain where the output has real-world consequences.
Package and setup
Up to £130k base
Equity
2 days per week in London
5 weeks per year working remotely from anywhere
Series A startup
Hiring multiple Lead Software Engineers
What they need
Strong Python engineering experience
Lead-level ownership
Strong backend, product, and systems judgement
Ability to build production-quality software quickly
Comfortable working across complex technical domains
No electronics background required
Interest in applied AI and real-world engineering problems
This is a strong opportunity for Python engineers who want applied AI, real technical depth, and flexibility that actually means something.
Outro
FastAPI is a brilliant tool, but production success is not about the framework name.
It is about understanding the system around it.
Async behaviour, blocking calls, database pools, worker processes, p95 latency, observability, background jobs, and deployment strategy are where senior Python judgement shows up.
Open question for debate: what is the most common FastAPI production mistake you have seen, async misuse, database pooling, background tasks, or poor observability?
Hiring? Contact
Josh Smith
Email: [email protected]
Phone: 01727 225 552
