← Back to list

Missing Semester in Python Backend Development: From Socket Layer, toward ASGI, to FastAPI/Django

Modern Python frameworks are incredibly productive.

HichamElMefeddel · 2026-02-11 20:16 · 7 claps · 6.7 min read
#python #backend-development #asgi #fastapi #django
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🔒 · Cybersecurity

Missing Semester in Python Backend Development: From Socket Layer, toward ASGI, to FastAPI/Django

OS Diagram Explaining Backend from low-level perspective

OS Diagram Explaining Backend from low-level perspective

Modern Python frameworks are incredibly productive.

You write a few decorators, define some async functions, run uvicorn, and suddenly you have a production-ready HTTP server handling thousands of concurrent connections.

But there’s a missing layer of understanding for many backend engineers:

What actually happens between a TCP packet hitting your machine and your FastAPI endpoint receiving a Request object?

This article fills that gap.

We’ll walk step by step:

  • From raw sockets
  • To non-blocking I/O
  • To event loops (epoll/kqueue)
  • To HTTP parsing
  • To WSGI vs ASGI
  • To how modern Python frameworks are structured

This is the missing semester of Python backend development.

1. Everything Starts With a Socket

Everything starts with a socket

Everything starts with a socket

At the lowest level, web servers are just programs that:

  1. Open a TCP socket
  2. Bind to an IP and port
  3. Listen for connections
  4. Accept clients
  5. Read bytes
  6. Write bytes back

In pure Python:

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8000))
server.listen()

while True:
    conn, addr = server.accept()
    data = conn.recv(1024)
    conn.sendall(b"HTTP/1.1 200 OK\r\n\r\nHello World")
    conn.close()

That’s it. That’s a web server. But this server is:

  • Blocking
  • Single-threaded
  • Not scalable
  • Not parsing HTTP correctly
  • Not concurrent

Still, this is the foundation. Every framework you use ultimately depends on this primitive.

2. The Blocking Problem

Blocking problem

Blocking problem

The problem with the previous server is simple:

accept() blocks. recv() blocks.

If one client is slow, everyone waits.

There are traditionally three solutions:

  1. Multi-threading
  2. Multi-processing
  3. Non-blocking I/O + Event Loop

Modern Python async frameworks choose option 3 due to the feasibility of it. If we choose multi-threading then creating multi threads in our OS is a very expensive operation.

Choosing the multi-processing has even more expensive effects, forking for each client to handle it can exhaust our OS resources.

Then the idea of non-blocking I/O + Event Loop raises up, can we continue with one thread and have a syscall that let us know whenever there is a READ or WRITE event in our socket fds we’re tracking.

3. Non-Blocking I/O and the OS

Operating systems provide mechanisms to monitor many file descriptors (sockets) at once:

  • epoll (Linux)
  • kqueue (BSD/macOS)
  • IOCP (Windows)

These allow you to ask the OS:

“Tell me which sockets are ready for reading or writing.”

Instead of blocking on one socket, you block on a selector.

Python exposes this via:

import selectors

Under the hood, this module uses epoll or kqueue depending on your OS.

This is where the real scalability begins.

4. Event Loops: Abstracting syscalls

Event loops, abstracting syscalls

Event loops, abstracting syscalls

Event loops are the core backbone of I/O backend operations in today’s asynchronous backend ecosystem.

An event loop is essentially:

  1. Register sockets
  2. Ask OS which are ready
  3. Dispatch callbacks
  4. Repeat forever

In simplified terms:

while True:
    events = selector.select()
    for key, mask in events:
        callback = key.data
        callback()

That’s the core of asynchronous execution.

Frameworks don’t manage syscalls directly, the event loop does.

In Python, this is abstracted by asyncio module.

And even more efficiently by: uvloop (written in C, powered by libuv), the event loop abstracts:

  • epoll
  • kqueue
  • file descriptor readiness
  • scheduling coroutines
  • cooperative multitasking

It does the heavy lifting so your async def function can look clean.

5. Coroutines and Cooperative Concurrency

Coroutines and cooperative concurrency

Coroutines and cooperative concurrency

When you write:

async def handler():
    await database_call()

What actually happens?

  • The function becomes a coroutine.
  • It yields control when hitting await.
  • The event loop schedules another task.
  • When the I/O is ready, the coroutine resumes.

No threads. No context switching at the OS level. No preemption.

This is cooperative concurrency, the foundation of ASGI frameworks.

6. HTTP Is Just Text

Before frameworks exist, you must understand:

HTTP is just text over TCP.

Example request:

GET /hello HTTP/1.1
Host: localhost
User-Agent: curl/7.79.1

You must parse:

  • Method
  • Path
  • Headers
  • Body

Libraries like: httptools orh1 handle this parsing efficiently, but fundamentally, you’re just reading bytes and interpreting them.

7. WSGI: The Old Standard

Before ASGI, there was WSGI.

WSGI defined a simple interface:

def app(environ, start_response):
    ...

It works well for synchronous apps.

But it has limitations:

  • No native async
  • No WebSockets
  • No long-lived connections

It assumes blocking execution.

That’s why ASGI was introduced.

8. ASGI: The Async Gateway Interface

ASGI generalizes WSGI for async environments.

An ASGI app looks like:

async def app(scope, receive, send):
    ...

Three core components:

  • scope → connection metadata
  • receive → async function to receive messages
  • send → async function to send messages

Example minimal ASGI response:

async def app(scope, receive, send):
    await send({
        "type": "http.response.start",
        "status": 200,
        "headers": [(b"content-type", b"text/plain")],
    })
    await send({
        "type": "http.response.body",
        "body": b"Hello ASGI",
    })

Notice:

  • No sockets
  • No epoll
  • No manual parsing
  • No threading

The abstraction is thin, but powerful.

9. Where Uvicorn Fits

Where uvicorn fits

Where uvicorn fits

Uvicorn is:

  • An ASGI server
  • Powered by asyncio or uvloop
  • Uses httptools for HTTP parsing

It handles:

  • TCP connections
  • HTTP parsing
  • Event loop orchestration
  • Translating requests into ASGI events

Your framework just implements the ASGI contract.

10. Building a Minimal Framework

When you build a minimal FastAPI-like framework, you’re essentially implementing:

  1. Routing
  2. Dependency Injection
  3. Request abstraction
  4. Response abstraction
  5. Middleware
  6. Lifespan events

That’s it. The server (Uvicorn) does the hard part. Python ecosystem excels at the interfaces and abstracting stuff, that’s one of the reasons why there are plenty of libraries out there, it’s due to the simplicity and level of abstraction exists in Python.

For now, we moved from the socket layer, asgi layer toward framework internals, next up are some framework-internals sections to discuss each concept.

11. Routing: Trie for O(N) Path Resolution

Trie based routing

Trie based routing

Instead of naive string matching, you can use a Trie.

Why?

Because routes share prefixes:

/users
/users/{id}
/users/{id}/posts

Trie advantages:

  • Shared prefixes reduce duplication
  • O(N) lookup based on path length
  • Clean parameter extraction

This makes routing scalable and elegant.

12. Dependency Injection and Registry Design

Dependency injection and registry design

Dependency injection and registry design

Frameworks feel powerful because of composition.

A registry-based dependency system:

  • Maps types → providers
  • Resolves dependencies recursively
  • Caches scoped instances

It’s not magic.

It’s controlled object graph resolution.

Once you build it yourself, you realize how thin the abstraction is.

13. The Big Realization

From socket layer, to asgi layer, to frameworks/libraries layer

From socket layer, to asgi layer, to frameworks/libraries layer

The biggest surprise? It’s not that hard. We tend to imagine frameworks as giant black boxes.

In reality:

  • The OS handles I/O multiplexing.
  • The event loop schedules coroutines.
  • The ASGI server translates HTTP into messages.
  • The framework wires routing and dependency resolution.

Each layer is focused. Each abstraction is thin. Each piece is composable. The ecosystem does the heavy lifting.

14. Why This Matters

Understanding this stack changes how you:

  • Debug production issues
  • Optimize performance
  • Design APIs
  • Evaluate frameworks
  • Build internal tools

You stop treating frameworks as magic meanwhile you start treating them as layered systems.

And layered systems are understandable.

15. The Missing Semester

Many backend engineers know:

  • How to write endpoints
  • How to connect to a database
  • How to deploy to production

But they don’t know:

  • How epoll works
  • How event loops schedule tasks
  • What ASGI really does
  • Why async improves throughput
  • How routing resolution works internally

That’s the missing semester.

16. Final Thoughts

Modern Python backend development is a beautiful composition of:

  • OS primitives
  • Event-driven architecture
  • Protocol design (ASGI)
  • Clean abstractions

Once you walk from:

Socket → Non-blocking I/O → Event loop → HTTP parsing → ASGI → Framework layer

You realize:

Frameworks are not magic. They are elegant orchestration and building a minimal one yourself might be the best way to truly understand backend systems.

If you’re curious about going deeper, try building:

  • A socket server
  • A selector-based event loop
  • A minimal ASGI app
  • A Trie-based router
  • A simple dependency injection container

It will change how you think about backend engineering.

This is the semester most of us never had.

But it might be the most interesting one, the one that gives you most of the knowledge needed for strong backend foundations

Last words …

Last thing to say, I believe in the idea of building scratch from scratch, building every technology you interact with is the most efficient way to master it.

You may have a different opinion due to the concept of abstraction in computer science, but with AI today, the level of abstraction is very high in the level that we can’t think about the underground fundementals.

I already did so, you can find my implementation for FastAPI version named microapi: https://github.com/hel-mefe/microapi

I’m thinking of making every component involved in the backend ecosystem from scratch, making an event loop program similar to uvicorn, framework that already exists, built my own named MicroAPI similar to FastAPI … etc

Drop a comment with your favourite project, I will be glad to continue this serie of missing semester, build scratch from scratch.


메타데이터
post_id
e47abb9b9d1f
slug
missing-semester-in-python-backend-development-from-socket-layer-to-asgi-e47abb9b9d1f
url
https://medium.com/@hichamelmefeddel/missing-semester-in-python-backend-development-from-socket-layer-to-asgi-e47abb9b9d1f
canonical_url
https://medium.com/@hichamelmefeddel/missing-semester-in-python-backend-development-from-socket-layer-to-asgi-e47abb9b9d1f
author_url
https://medium.com/@hichamelmefeddel
status
ok
fetched_at
2026-06-22 05:41:33