Theme

Production performance diagnosis for Python engineers

Find the bottleneck.

Layer by layer, from the API down to the database.

The course is being built. Leave your email and I will tell you the day it opens.

No payment now, and no email other than the one announcing the opening.

Written by a backend engineer: ten years of Python, five of them freelancing, at Alma, Back Market, among others.

  • Python
  • FastAPI
  • PostgreSQL
  • Redis
  • OpenTelemetry

Stop guessing. Diagnose first, fix second.

First, find where the problem comes from. Measure, isolate, prove: you point at the cause and show the numbers behind it, instead of arguing about likely suspects.

Then, fix it, with the limits stated. No course covers every possible fix. What you get: pointers, the notions each fix rests on, and the bottlenecks that come up again and again.

Throughout, chapters that stand on their own, each recapping what it needs. Quizzes, real cases on your own stack, cheat sheets to keep.

The tools you will know how to read

Reading a query

Read a SQLAlchemy query and know, before running it, what it will ask the database and how many times.

Browser dev tools

Interpret what the dev tools already tell you. There is a lot to learn there before you open a single profiler.

Profilers

Use several profilers, deterministic and statistical, and know what each one sees, what it misses and what it costs.

Visualising a profile

Turn a raw profile into something you can read: flame graphs, call trees, and the frame that actually holds the time.

Import time

Monitor what your application pays on import, and cut down how long it takes to start.

Query profiler

Read what a request really sent: spot an N+1, a full scan, a query that runs long, and the plan behind it.

The method

Measure, isolate, prove, fix, instrument

Five steps, always in that order. Skipping one is how a week disappears into the wrong layer.

  1. 01

    Measure

    Find where the time actually goes, before forming any opinion about it.

  2. 02

    Isolate

    Narrow it down to one layer, one component, one call. Rule the others out with a number.

  3. 03

    Prove

    Confirm the hypothesis with a profile, a query plan or a trace. A plausible cause is not a cause.

  4. 04

    Fix

    Make the smallest change that moves the measurement, then measure again.

Who it is for

This is a course for people who already ship

This is for you if

  • You write Python and maintain APIs in production
  • You are the one who gets asked why an endpoint got slow
  • You work with FastAPI, Django, Flask or something close
  • You know SQL, and want to get much better at reading what the engine did
  • You have access to logs, traces or metrics, or can put them in place
  • You want a method you can repeat, not a list of tricks

This is not for you if

  • You are learning Python
  • You have never built an API
  • You are looking for a beginner FastAPI course
  • You only want a list of optimisations to apply blindly
  • You are after computer science theory rather than production practice

A real investigation

One endpoint, measured before and after

Same data returned, same machine, same code path. The only difference is that someone looked at where the time went instead of guessing.

Before

GET /stories/naive?limit=50

SQL queries per request
51
p50
64 ms
p99
154 ms
throughput
152 req/s
What the numbers said

The query count follows the page size:

21 queries at limit=20

51 queries at limit=50

101 queries at limit=100

A slope of exactly 1 per row is the signature of an N+1.

One SELECT for the list, then one more per author.

No need to read the code to know that.

After

GET /stories/fast?limit=50

SQL queries per request
2
p50
14 ms
p99
26 ms
throughput
698 req/s

Measured with ab -n 600 -c 10 against FastAPI over PostgreSQL 16, 500,029 rows. Commands and raw output are the ones used throughout the course.

Seven chapters, one method

Learn the vocabulary, measure, find the component at fault, fix it on real cases, then instrument so it never surprises you twice.

what one number hides MEAN 84 · MEDIAN 60 1,000 requests, one endpoint p50 60 ms p75 64 ms p90 72 ms p95 81 ms p99 2,400 ms max 3,100 ms median 60 ms mean 84 ms nineteen requests in a thousand went past two seconds they moved the mean by 24 ms and the median by nothing the mean describes no one: half your users see 60 ms

Chapter 01

The words and the map

Before measuring anything: what a p99 is, what wall clock time hides, and where the time can go between a click and a row.

  • Mean, median, p50, p95, p99, standard deviation: what each says and what it hides
  • Wall clock time against CPU time, latency against throughput
  • The path of a request: front end, network, API, cache, database, third parties
  • Where time can lodge at each hop, and which tool exposes it
PercentilesCPU timeArchitectureVocabulary

Chapter 02

Profiling the API

Deterministic profilers record every call and slow the run down. Statistical ones sample and cost almost nothing. Knowing which to reach for is the skill.

  • Read the browser dev tools first: often the answer is already there
  • Deterministic and statistical profilers: what each sees, misses and costs
  • cProfile, pyinstrument, py-spy, and attaching to a process in production
  • Wire a profiling middleware into your routes, and keep it off the hot path
  • Decide from the profile whether you are IO bound or CPU bound
  • Visualise the result: flame graphs, call trees, own time against cumulative
  • Profile SQLAlchemy and import time
cProfilepyinstrumentpy-spyFlame graphs
a 50 ms wait, written three ways 8.6x how the handler is written req/s p50 async def + time.sleep() 18.23 548 ms def (threadpool) 134.3 59 ms async def + await asyncio.sleep() 156.51 55 ms a blocking call in a coroutine: 8.6x less throughput one request alone shows nothing: all three answer in 50 ms only concurrency reveals it on blocking code, def beats async def

Chapter 03

Hands on: fix it

You do not learn to diagnose by watching someone else do it. This chapter hands you broken endpoints and you fix them, one at a time, measuring before and after.

  • Real cases to fix yourself: IO bound, CPU bound, and the ones that are both
  • async def or def: what each does with your handler, and when def wins
  • What blocks the event loop, and how it looks in a profile before and after
  • Move IO to asyncio, move CPU to multiprocessing or a dedicated worker
  • Background tasks and fire-and-forget: what you gain, what you give up
  • The GIL and the event loop, explained through what you just measured
asyncioGILMultiprocessingWorkers
EXPLAIN (ANALYZE, BUFFERS) SEQ SCAN Limit (cost=14288.97..14291.89 rows=25 width=133) (actual time=24.272..25.953 rows=25 loops=1)-> Gather Merge Workers Launched: 2-> Sort Sort Key: score DESCSort Method: top-N heapsort Memory: 35kB-> Parallel Seq Scan on storiesFilter: ((status)::text = 'pending'::text)Rows Removed by Filter: 166349Buffers: shared hit=10674 Planning Time: 0.594 ms Execution Time: 25.992 ms 499,047 rows discarded to return 25 (166,349 × 3 loops: 2 workers plus the leader) · 500,029 rows CREATE INDEX … (score DESC) WHERE status = 'pending' Execution Time: 0.125 ms · Buffers: 26 16 kB index → 208× faster

Chapter 04

When it is the database

The profile is clear: your code is not the problem, the database is. Now what? This is where you learn to read what the engine actually did, and to change it.

  • Read an execution plan line by line: scans, joins, sorts, loops, buffers
  • Compare estimated against actual, and know what the gap means
  • Index types and when each applies: partial, composite, covering, GIN
  • Spot an N+1 by counting queries, without reading the code
EXPLAINIndexesN+1PostgreSQL

Chapter 05

Observability at scale

Instrument once, and stop hunting. A request that crosses several services should tell you where its time went before you have to go looking for it.

  • Instrument a Python stack with OpenTelemetry
  • Trace id, span id, context propagation: follow one request across services
  • Workers, queues and microservices: keep the thread of a request through all of it
  • Dashboards that fit on one screen, and alerts on signals that matter
OpenTelemetryTrace idSpan idAlerting
ab -n 600 -c 10 · /stories/naive p99 154 ms Percentage of the requests served within a certain time (ms) 50 % 64 ms 66 % 65 ms 75 % 66 ms 80 % 67 ms 90 % 69 ms 95 % 73 ms 98 % 76 ms 99 % 154 ms 100 % 156 ms p99 = 154 ms, but only ~6 samples sit above it Requests per second : 152.54 · mean : 65.6 ms A 65 ms average shows none of those 154 ms. fixed version: p99 = 26 ms · 698.31 req/s

Chapter 06

Load testing

A load test tells you where the tail sits and what gives way first. It does not tell you what your users are living. This chapter is about that difference.

  • Design a scenario that resembles your traffic, not a synthetic best case
  • Run it and read what comes out: throughput, percentiles, error rate
  • Find the breaking point, and what gives way first
  • Tell a real regression apart from noise in the numbers
Load testingThroughputPercentilesBreaking point
the default thread pool holds 40 tokens 40 → 100 $ uv add httptools uvloopimport anyiofrom contextlib import asynccontextmanagerfrom fastapi import FastAPI@asynccontextmanagerasync def lifespan(app: FastAPI):    limiter = anyio.to_thread.current_default_thread_limiter()    limiter.total_tokens = 100    yieldapp = FastAPI(lifespan=lifespan) every def endpoint shares that pool so does every sync driver call you left in a coroutine raise it, or move the work off the loop entirely a chapter of small levers, each with its measurement

Chapter 07

Tips, tricks and going further

The things that do not fit a chapter of their own, and the toolkit you keep after the course.

  • Track down a memory leak, and tell it apart from a cache filling up
  • git bisect on a performance regression: script the measurement, let it find the commit
  • Tune Uvicorn, Gunicorn and Hypercorn: workers, processes, what to set them to
  • FastAPI tips that pay off, and the traps that cost
  • Compression, HTTP/2 and QUIC: what each changes for an API
  • When leaving Python pays: Rust, Cython, and when it does not
  • Speed up your own test suite
  • Build the scripts, prompts and cheat sheets you will reuse on your own stack
MemoryUvicornFastAPITooling
Téva Krief

Who wrote it

Téva Krief

Backend engineer, ten years of Python

@teva_krief

Ten years of Python, five of them freelancing: a payments company, a marketplace, a Swiss insurer, and my own SaaS products shipped end to end. Different stacks, different team sizes, the same recurring scene: an endpoint slows down, everyone has a theory, and a week goes into the wrong layer. What I teach here is what I ended up doing instead, on real production systems: measure first, isolate, prove it, and only then change the code. I build my own tooling for backend analytics and API response times, and I teach diagnosis the way I run it.

Writing Python was never the scarce part

It gets less scarce every year. Plenty of people can write a FastAPI endpoint, and plenty of tools will write one for them. What almost nobody can do is take an endpoint that is slow in production, work out where the time actually goes, and prove it with a number.

For you

A skill you can name, prove, charge for, and take with you

Being the person who finds the cause of a slowdown is rare, and it shows immediately. In an interview, in a performance review, on a freelance quote: you are not saying you know Python, you are saying you take an endpoint apart and come back with the layer, the number and the fix. Python runs in production almost everywhere — payment systems, marketplaces, insurers, data platforms, internal tooling — so none of this is tied to one company. Learn it once, use it on the job you have now and on the one after that.

  • A repeatable method you can apply on any stack, from day one
  • Techniques to catch problems in production, on a running system
  • The Python deep dive few people have
  • Portable: it leaves with you when you do
  • Figures you can put in a review, a quote or a proposal
Checkout went from 2.1 s to 140 ms. The profile showed the request sitting on send_email, and nothing in the response depends on that mail leaving: it moved to a FastAPI background task, fire and forget. Here is the profile before, and here it is after.

For your team

One method, instead of five opinions per incident

If you run engineering, the cost is not the slowdown itself: it is the three engineers investigating three different hypotheses, the infrastructure added to buy silence, and the one person every performance question ends up on. Training the team on one shared method is how that stops.

  • “The API is slow” stops being an opinion and becomes a measurement
  • Investigations that reach a cause faster, with less guesswork
  • Either a real bottleneck to fix, or capacity you stop paying for
  • Autonomy across the team, instead of one performance expert
  • Engineers who want to take it: the skill counts on their own CV too
  • Onboarding: a new engineer diagnoses without a senior beside them

The expensive part isn’t the course. It’s guessing.

Performance problems become expensive when teams debug them without evidence.

2 days

Optimizing the wrong thing

A slow endpoint looks like a database problem. You add indexes, rewrite queries, and change the ORM. The real bottleneck was somewhere else.

€400 / month

Paying for a problem you never found

The service is slow, so you scale the infrastructure. It gets better until it doesn’t. You are now paying for capacity instead of understanding the bottleneck.

4 engineers

Four people, four hypotheses

Everyone has a theory about what is slow. Without the right profiling and observability skills, the team is debugging blindly.

Every time

The same investigation, from scratch

Nothing was instrumented and nothing was written down, so the problem comes back six months later and one senior engineer is pulled off their roadmap to find it again.

In preparation

Two ways to learn it

The same recorded course sits under both. One you take on your own; the other is run with your team, with the sessions and the questions that go with it.

For individual engineers

Learn the skill.

Stop guessing. Learn how to work out where the time in a slow back end actually goes, and how to prove it.

€890

Self-paced. Lifetime access, every update included.

  • The complete video course, every module
  • Hands-on exercises against a running application
  • Performance debugging case studies
  • Profiling and observability material
  • Templates, checklists and reference sheets
  • Every update, for as long as the course exists

Nothing to pay today: the course is being built. Joining the list holds the launch price for you.

For engineering teams

Train the team.

Train your engineers on one shared method for diagnosing production performance problems, instead of one opinion per engineer.

From €7,500

Final pricing depends on team size and training scope.

  • The full video course, for every engineer on the team
  • A private team workspace, each engineer at their own pace
  • Two to three live sessions with the instructor
  • Private Q&A on problems from your own production
  • Shared team resources, and where the team stands
  • Light adaptation of the examples to your stack

This is a proven core training with live support around it, not a course written from scratch for your company. The videos and the learning path stay the same for everyone; what adapts is the examples discussed, the exercises, and the problems you bring to the sessions.

Tell me the size of the team and what you are running. I answer with a scope and a price.

Why these numbers

Priced against engineering time

Not against hours of video. The unit of comparison is the senior days nobody spends in the wrong layer, and the instance nobody upgrades.

Bought once, kept for good

Lifetime access and every update. The stack moves, and you keep the version that matches what you actually run in production.

The team price is not a stack of seats

It buys access to the instructor: live sessions, private Q&A, and your own slowdowns taken apart with the method in hand. That is the part a recording cannot do.

What the course itself contains

The same recorded material, under both offers.

The course

  • HD video, chaptered
  • Full written version of every lesson
  • French and English, written and subtitled
  • Standalone chapters with recaps
  • Animated explanations of each mechanism

The practice

  • Common causes, sorted by symptom
  • Real cases run against your own application
  • A quiz per chapter
  • Cheat sheets

The tooling

  • OpenTelemetry boilerplate for FastAPI, and what to change for Django or Flask
  • List of great tools to profile
  • Diagnostic scripts