Which Framework, When
1 · The lesson
readYou've now built apps in all three of Python's major web frameworks: Flask, FastAPI and Django. Sooner or later, every developer has to answer the question: which one should I use for the next project? The internet is full of strong opinions, and many of them come from people who know only one of the three.
This lesson compares the three frameworks fairly: the trade-offs, a practical decision tree, and the cases where each framework is the best choice. It does not say which one is "best" overall, because you can't answer that without knowing what you're building.
Run locally: there is no code in this lesson, because it is about making a decision. The 🎯 Your Turn section asks you to use the decision tree on five realistic project briefs.
1. The Comparison Table
| Dimension | Flask | FastAPI | Django |
|---|---|---|---|
| Style | Minimal, you assemble | Modern, async-first | Batteries-included |
| Best for | Custom apps, learning the HTTP layer | API-only services, async I/O | Content sites, admin-heavy, complete apps |
| Learning curve | Low | Low–medium (Pydantic + async) | Medium–high (the Django way) |
| Async | Bolted on (2.x supports async def views) | Native | Bolted on (async views since 3.1, async ORM 4.1+, still maturing) |
| ORM | Bring your own (SQLAlchemy usually) | Bring your own (SQLAlchemy/SQLModel) | Django ORM (excellent, deeply integrated) |
| Admin UI | None | None | Built-in (the Django admin) |
| Auth | Bring your own (Flask-Login + Flask-Bcrypt) | Bring your own | Built-in (django.contrib.auth) |
| API docs | Bring your own (apispec, flasgger) | Auto (OpenAPI + Swagger UI from type hints) | DRF + drf-spectacular |
| Templating | Jinja2 (default) | Jinja2 (optional) | Django templates (built-in) |
| Forms + CSRF | Bring your own (Flask-WTF) | Bring your own | Built-in |
| Migrations | Add Alembic | Add Alembic | Built-in (makemigrations) |
| Real-time perf (RPS) | Medium | Highest (uvicorn ASGI) | Lowest (sync WSGI by default) |
| Community size | Massive, mature | Fast-growing, very active | Massive, oldest of the three |
Two things to read from this table:
Django has the most pre-built features. Auth, admin, ORM, migrations, forms, CSRF and sessions all come with it. For a "build an MVP alone" or "internal tool with logins" project, a large part of the work is already done before you would have finished setting up the same features in Flask.
FastAPI has the highest maximum performance and is the easiest to use for APIs. Native async, automatic OpenAPI, Pydantic validation. For a JSON API consumed by an SPA or mobile app, it is the usual modern choice.
Flask is the easiest to keep in your head and gives you the most freedom. It has a small core and makes few decisions for you. If you want to understand every line of your stack, or you're maintaining a service that's already in Flask, it's still excellent.
2. The Decision Tree
Go through the questions in order. Stop at the first one where the answer is yes.
- Do you need an admin UI for non-technical users to manage content/data?
admin.site.register(Model).
- Are you building a backend API for an SPA, mobile app, or other API client, with no server-rendered pages?
- Do you want to learn how web frameworks work inside, or teach someone the basics of HTTP?
- Is this a high-concurrency I/O-bound service, with many DB queries, external API calls, websockets or streaming?
uvicorn. For this kind of work, native async handles many more requests per second than sync WSGI.
- Is it a content site with CMS-like features, marketing pages, blog posts or structured editorial content?
- Do you have an existing Flask codebase that you can't replace?
- Microservices: many small services, each responsible for one area, deployed independently?
- Are you a solo founder building a full-stack MVP quickly, with everything included?
- Mostly server-rendered HTML, with a few small interactive parts (HTMX, Alpine, Stimulus)?
{% extends %} and the admin work well together for this.
- You already have a strong opinion about all three?
3. The Real-World Picture in 2026
What people are building and maintaining today:
- FastAPI is the most popular choice for new API-first products. It is a common choice at AI startups, fintechs, and companies founded after 2022 that have a React frontend and a Python backend. Many open-source AI tools use it for their inference servers.
- Django is the most popular choice for content sites, internal tools, marketplaces and Python monoliths. Because of the admin, it is hard to replace in any product where non-engineers manage data. Instagram, Mozilla, Disqus and Eventbrite have all used Django at large scale.
- Flask has a smaller share, but it is still used very widely: in older systems that worked fine and were never rewritten, in internal tools at engineering-heavy companies, in new "I want full control" projects, and in teaching, because it is the easiest to teach. Netflix and Lyft have both used Flask in production.
None of them is dying, and none of them is clearly "the best". Python has three very good web frameworks, and they overlap less than they seem to.
4. What Is Different, and What Isn't
Most of the things that seem to depend on the framework don't:
- All three can serve HTML, return JSON, talk to Postgres, scale to 100K requests/minute behind a load balancer, deploy on Kubernetes, run on Lambda, talk to Redis, and authenticate users. What the framework can do is rarely the limit.
- All three have great test ecosystems.
pytestworks with all of them. - All three have good typing support (Flask has type hints built in since version 2.0, Django uses
django-stubs, and FastAPI is built on type hints). - All three deploy the same way in 2026: uvicorn/gunicorn behind nginx, or a managed PaaS, or a serverless function.
What is really different:
- How much you write yourself versus how much comes pre-decided. Django decides, and Flask doesn't.
- Async support. FastAPI was built for async, but Django and Flask added it later.
- Built-in admin. Django has it, and the other two don't.
- Validation. FastAPI's Pydantic + type hints approach is easier to use than the other two.
Once you know this, "which framework?" becomes a small set of practical trade-offs, not a matter of opinion.
5. What This Lesson Is Not
This is not a verdict that one framework is "better". If someone tells you "always use X" without asking what you're building, they are wrong. The same engineer can, and probably should, use different frameworks for different problems.
In particular, three claims that show up in framework debates online and are wrong:
- "Django is too heavy / old." Django is a complete framework. Its size comes from features you would otherwise build yourself. In software, "old" often means "tested in real use for years, and stable". Instagram serves a billion users on Django.
- "FastAPI is just hype / not production-ready." FastAPI has been in production at Uber, Netflix, Microsoft, and most AI startups for years. It is production-ready.
- "Flask is dead." Flask 3.x exists, is actively maintained, and runs at scale at large companies. Compared with FastAPI, its share is smaller than it once was, but it is still used very widely.
The framework affects how fast and how easily you build, not what you can build. Choose the one that makes your problem easier today, knowing you can migrate pieces later if needed.
6. The Migration Question
A common worry is: "what if I pick the wrong one and have to migrate?" Three things to know:
1. Migrations between Python frameworks are rare in practice. Most apps that succeed stay on their original framework forever. The cost of rewriting is almost always higher than the cost of living with a slightly imperfect choice.
2. When migrations do happen, they're incremental. You usually don't rewrite. You run the new framework next to the old one, behind the same load balancer, with new endpoints on the new stack. Both run for years.
3. The portable pieces are most of the code. Your business logic, your models (especially if you use SQLAlchemy across all three), your tests, your queries: these don't change. What changes is the routing layer, the view functions and the serialisation layer. This is a lot of work, but it has clear limits.
So pick what fits today. Don't worry too much about being locked in. Well-written code is portable, and the framework is the thinnest layer of the stack.
7. Common Mistakes
1. Copying others without thinking. "Use FastAPI because it's popular / Hacker News says so", when Django would deliver the MVP much faster. The framework that's right for a 100-engineer team building a high-RPS microservice is not necessarily right for a solo founder building a CRUD app.
2. Resisting Django ORM because it's "not SQLAlchemy". The Django ORM is excellent. It gives you less low-level control than SQLAlchemy Core, and its query builder is less flexible for very complex cases. But for most CRUD work, it is simpler and faster to use. Use the framework's defaults unless you have a concrete reason not to.
3. Building a CRUD admin in Flask instead of using Django. Three weeks of building Flask admin pages with Flask-Admin + custom permissions could have been one afternoon with admin.site.register(Model). If your project needs an admin, that is a strong reason to choose Django.
4. Using Django for a single API endpoint. Setting up a Django project for one webhook receiver is too much. A 20-line FastAPI service or a 15-line Flask app does the job and deploys in a Lambda.
5. Picking by "performance benchmarks" alone. Hello-world benchmark RPS rarely predicts real-world performance. The database, the network and serialisation usually take most of the time. The framework whose ergonomics let you ship the right architecture matters far more than the framework whose hello-world is 30% faster.
6. Refusing to learn the other two. Even if you only use one professionally, knowing the others helps you understand what your framework is doing (and what good defaults look like). A senior Python engineer should be able to read code in all three.
🎯 Your Turn: Pick the Right Framework
Below are five realistic project briefs. For each one, pick Flask, FastAPI, or Django (or Django+DRF) and write one sentence justifying the choice. Some have more than one reasonable answer. What matters is that your reason is based on real trade-offs, not on a feeling.
Brief 1: INTERNAL ADMIN TOOL A 50-person company needs an internal tool to manage customer records, invoices, and support tickets. Five engineers will build it; the users are mostly non-technical (sales, support, finance). It needs auth, permissions, basic dashboards, and full CRUD on every table. Brief 2: REAL-TIME CHAT SERVICE A chat backend with websockets, presence, typing indicators, and 50K concurrent connections. Stateless API otherwise; data lives in Redis and Postgres. One frontend team (React + iOS + Android) consumes it. Brief 3: MARKETING WEBSITE WITH NEWSLETTER SIGNUP A small marketing site for a startup, with an about page, blog, contact form, newsletter signup. Content updated weekly by the marketing team (non-technical). SEO matters; performance matters. No app, no users beyond the marketing team's CMS access. Brief 4: ML INFERENCE API A FastAPI-or-not decision. A single endpoint that loads an ML model, accepts JSON input, runs inference, and returns JSON. Will need to scale to 1000 RPS. Most of each request's time is spent in the GPU call. No frontend, no auth (internal network only). Brief 5: LEGACY SAAS WITH 200K LINES OF FLASK An existing 200K-LOC Flask 2.x codebase that ships a B2B SaaS app. Stable revenue, no plans to rewrite. You're joining the team and the product roadmap is steady feature delivery. You're asked whether to "modernise" by rewriting in Django or FastAPI.
Hint 1: Match the question, not the brand
Re-read the decision tree in Section 2. For each brief, find the first question where the answer is yes. Brief 1's "admin UI for non-technical users" clearly points to Django. Brief 2's "websockets + high concurrency" clearly points to FastAPI. Don't overthink it. The questions are designed to give you a clear first answer.Hint 2: The legacy brief is a trap
Brief 5 isn't really about choosing a framework. It's about "should we rewrite?", and the answer is almost never yes. Base your reason on the cost of migrating compared with the small benefit, not on which framework you'd pick for a new project.Show full solution
These are the standard answers. People can disagree on some details, but each brief has a clear best fit.
Brief 1: INTERNAL ADMIN TOOL → Django
The admin UI alone makes this an easy decision. admin.site.register(Customer), admin.site.register(Invoice), admin.site.register(Ticket), customise list_display / list_filter / search_fields, and you have most of the product. Auth is built in. Permissions come from Django groups. Migrations are included. Five engineers using Django will build this much faster than five engineers using Flask or FastAPI, because they don't have to build any of the basic pieces.
Brief 2: REAL-TIME CHAT SERVICE → FastAPI
Websockets, 50K concurrent connections, async everywhere: this is exactly what FastAPI + uvicorn is built for. Django Channels can do websockets too, but the rest of the brief (stateless API, separate frontend teams, no admin needed) doesn't benefit from Django's built-in features. FastAPI lets you write async def everywhere, scales well by adding more servers, and the OpenAPI docs help the three frontend teams stay in sync on the schema.
Brief 3: MARKETING WEBSITE WITH NEWSLETTER SIGNUP → Django (or Wagtail on Django)
A content site that is updated weekly by non-technical editors is exactly what Django is best at. The admin is where marketing edits the blog. Django's templating handles the rendered HTML. If the content modelling gets richer (structured pages, navigation menus, asset library), move to Wagtail (a CMS built on Django). It uses the same framework underneath, with many more CMS features. Flask works too, but you'll spend the first week building "admin for marketing to edit blog posts" that Django already gives you.
Brief 4: ML INFERENCE API → FastAPI
Many requests must wait on a slow GPU call at the same time, and FastAPI is built for that kind of concurrency. (The model call itself is blocking, so run it in FastAPI's thread pool, for example in a plain def endpoint, not directly inside an async def one, or it stops the event loop.) High RPS target, single JSON endpoint, no admin, no auth. Pydantic for the request/response schema documents the API for the internal callers. This is a typical FastAPI use case. Django for one endpoint would be too much. Flask is fine, but FastAPI's async + automatic OpenAPI make it the better choice here.
Brief 5: LEGACY 200K-LOC FLASK → Stay on Flask
Rewriting 200K lines of working code in another framework is a multi-year project that delivers no new customer value. The Flask ecosystem (Flask-Login, Flask-SQLAlchemy, Flask-Migrate, Flask-RESTful) covers everything Django and FastAPI cover. If you want to modernise, do it inside Flask: upgrade to Flask 3, use async views where they help, use pydantic for request validation if it helps, and add FastAPI next to Flask for any new services that benefit from async. But do not rewrite. The product is already working. The limit is how fast the team can build, and a rewrite slows the team down for a long time.
The main lesson: the right framework depends on the project. The same engineer can answer "Django" to brief 1 and "FastAPI" to brief 2 on the same day, without contradicting themselves. If someone gives the same answer for all five briefs, they are missing what each framework is good at.
What You Learned
- There is no universally-best Python web framework. Flask, FastAPI, and Django each win different cases. Pick by the problem, not by which framework you like.
- Django wins when you need an admin UI, you're building content-heavy or full-stack apps, or you want many built-in features to build an MVP quickly.
- FastAPI wins on API-only services, async-heavy workloads, microservices, and modern Pydantic-first development.
- Flask wins when you want minimalism, full control, are teaching the HTTP layer, or are maintaining an existing Flask codebase.
- Most "framework debates" are wrong because they assume one answer fits every project. The right choice depends on the project.
- What the framework can do is rarely the limit. All three can do almost anything. What differs is how fast and how easily you can build your specific project.
- Don't worry too much about lock-in. Migrations are rare and gradual, and the portable code (business logic, models, tests) stays.
- Knowing all three makes you a better engineer in the one you use most. It helps you understand what good defaults look like and what your framework does internally.
Next: once you've chosen a framework, the production concerns are the same for all three. See deployment, Auth & JWT, Postgres for production, and Docker for Python for what comes after "I picked a framework".
Found this useful? Share it with a friend who’s learning Python.