Why Tornado?

Author Joe Gasewicz

Published on 03 Apr 2026 16:11:36

Why Tornado Is Still The Best Choice For Developing Modern Async Python Applications

Tornado has always had this slightly stubborn reputation. It never tried to become the “cool” framework, never chased trends, never rebranded itself every couple of years. And that is exactly why it still matters, especially in a world dominated by names like Flask, Django, and FastAPI.

If you’ve spent any real time building async systems in Python, you eventually run into the same realization: abstraction is nice until it starts getting in your way. Tornado sits in a very particular place where it gives you just enough structure to move fast, but not so much that you lose control of how your system actually behaves.

That balance is rare, and it is something developers often only appreciate after hitting the limits of Flask, stretching Django beyond its comfort zone, or leaning heavily on the convenience of FastAPI.

It was async before async was fashionable

Tornado didn’t bolt async on later. It was designed around non blocking I O from the beginning. That shows up in small ways that are easy to miss until you compare it with newer frameworks like FastAPI, or async add ons for Flask and Django.

There’s no impedance mismatch. No layers pretending to be synchronous while secretly juggling coroutines underneath. What you write is what runs. That directness matters when you are debugging something subtle and need to understand exactly where your event loop is and why something feels off.

A lot of frameworks feel clean at first, then complicated once you go off the happy path. Django gives you structure, Flask gives you freedom, FastAPI gives you speed, but Tornado gives you something slightly different. It gives you clarity when things stop being simple.

It plays perfectly with the asyncio ecosystem

One of the quiet strengths of Tornado today is that it doesn’t try to own the ecosystem. It just plugs into asyncio and gets out of the way.

Unlike Django, which historically built its own conventions, or Flask, which leans on extensions, Tornado simply lets you use what already works well in Python.

You can bring in async database drivers, modern HTTP clients, Redis, streaming systems, without fighting the framework. You are not forced into “Tornado versions” of everything. You are just using Python.

That becomes incredibly valuable when your application grows beyond a single service and you start mixing tools across systems.

WebSockets and long lived connections feel natural

This is where Tornado still quietly outperforms most alternatives.

Real time systems are awkward in many frameworks. You can build them with FastAPI, or bolt them onto Django, but you often feel like you are adding complexity to something that was not originally designed for it.

With Tornado, WebSockets feel native. You write a handler, keep the connection open, and that’s it. No extra layers, no special abstractions, no hidden machinery.

For dashboards, live feeds, collaborative tools, or anything that depends on persistent connections, this simplicity is not just nice. It changes how confidently you can build.

It doesn’t hide the important parts

Some frameworks aim to reduce thinking. Tornado assumes you want to understand what you are building.

There is no heavy magic around request handling. No invisible dependency injection like in FastAPI. No deeply integrated ORM expectations like in Django. No reliance on a web of extensions like in Flask.

You see the request, you handle it, you return a response.

If you want middleware, you build it. If you want sessions, you design them. If you want performance, you control it directly.

That might sound like more effort, but in practice it gives you clarity. And clarity is what you need when things break.

It scales in a very honest way

Tornado does not promise magical scalability. It gives you efficient I O and expects you to design your system properly.

Many developers start with Flask or Django, and only later run into concurrency limits or architectural friction. Tornado pushes you into a non blocking mindset from the start.

You are already thinking in terms of async boundaries, external services, and distributed state. You are not rewriting your mental model later. You are already there.

The ecosystem is smaller, but sharper

It’s true that Tornado does not have the same plugin ecosystem as Django, or the extension culture of Flask, or the rapid growth around FastAPI.

But that turns out to be less of a problem than it sounds.

Instead of relying on Tornado specific packages, most real world applications use well maintained async libraries from the broader Python ecosystem. These tend to be more actively developed and more stable over time.

You end up with fewer abandoned dependencies and more control over your stack.

It still feels like Python

This is subtle, but important.

Tornado code tends to look like Python, not like a framework specific dialect. When you compare it to heavily structured Django apps or annotation heavy FastAPI code, Tornado often feels closer to the language itself.

That readability pays off, especially when projects grow and evolve.

A bit of reality

Tornado is not the easiest framework to pick up. It does not guide you as much as Django or FastAPI, and it does not hide complexity the way Flask sometimes can with extensions.

It expects you to understand async programming at least a little.

But that expectation is also its strength. It pushes you toward building systems that actually behave well under load, rather than just looking clean in development.

Final thought

Tornado is not trying to compete directly with Flask, Django, or FastAPI in terms of popularity. It is trying to stay reliable.

In a landscape where frameworks constantly evolve, repackage themselves, and add layers of abstraction, Tornado’s consistency is almost unusual.

It gives you the tools to build modern async systems without pretending the complexity doesn’t exist.

It is not flashy. It is not trendy.

It just works.