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 def is good for non-blocking I/O

  • normal def endpoints are run in a thread pool

  • blocking 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.

The hidden bottlenecks in FastAPI systems

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 def routes 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:

  1. “What is the difference between def and async def in FastAPI?”

  2. “What happens if you call requests.get() inside an async endpoint?”

  3. “How would you investigate high p95 latency?”

  4. “How do you choose worker count?”

  5. “How do you configure database connection pools?”

  6. “When would you move work out of the request lifecycle?”

  7. “How do you monitor event-loop lag or thread pool saturation?”

  8. “What are the limits of FastAPI’s BackgroundTasks?”

  9. “How do you prevent external APIs from slowing down your whole service?”

  10. “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