Я создаю веб-приложения на Python уже четыре года. Первые два года моим стандартом был Django. Последние два года в большинстве проектов его заменил FastAPI. Но я никогда не делал полноценного сравнения всех основных вариантов сразу.

Поэтому я реализовал один и тот же API на Django, FastAPI, Flask и Starlette. Одни и те же эндпоинты, одна и та же база данных, одна и та же бизнес-логика. Я измерил производительность под нагрузкой, скорость разработки, сложность кода и то, как каждый фреймворк справляется с продакшен-требованиями.

Один результат стал для меня полной неожиданностью. Представляю вашему вниманию его честный разбор.

Тест

В API было пять эндпоинтов: регистрация пользователя, вход с генерацией JWT, пагинированный список с обращением к PostgreSQL, детальный эндпоинт со связанными данными и эндпоинт с фоновой задачей, запускающей асинхронную работу.

Это все покрывает паттерны, которые встречаются почти в каждом реальном приложении: аутентификация, доступ к базе данных, пагинация, связанные данные и асинхронные операции.

Каждый фреймворк я запускал на одинаковом оборудовании с одинаковой конфигурацией базы данных. Нагрузочное тестирование проводилось с 100 одновременными пользователями в течение 5 минут.

Django

Django — самый полноценный Python-фреймворк из доступных. Он сразу поставляется с ORM, админ-панелью, аутентификацией, работой с формами и системой миграций.

Скорость разработки: Django позволил стартовать быстрее всего. Структура проекта прописана, инструменты для создания каркаса берут на себя большую часть шаблонного кода. Эндпоинты регистрации и входа заработали менее чем за 30 минут благодаря встроенной аутентификации Django.

Производительность под нагрузкой:

  • Среднее время ответа: 187 мс.
  • P95: 312 мс.
  • Запросов в секунду: 284.
  • Доля ошибок: 0,2%.

Добротно, но не исключительно. Синхронная архитектура Django означает, что каждый воркер обрабатывает один запрос за раз. При параллельной нагрузке запросы выстраиваются в очередь.

Сложность кода: Эндпоинт пагинированного списка со связанными данными получился чище всех четырех фреймворков. ORM Django элегантно обрабатывает связанные данные.

# Django - чистота и читаемость
def order_list(request):
    orders = Order.objects.select_related("user").prefetch_related("items")
    paginator = Paginator(orders, 20)
    page = paginator.get_page(request.GET.get("page", 1))
    return JsonResponse({"orders": list(page.object_list.values())})

Где возникли сложности: Эндпоинт с фоновой задачей потребовал Celery для корректной обработки асинхронных заданий. Это дополнительная зависимость, дополнительная инфраструктура и дополнительная настройка, в то время как другие фреймворки справляются с этим более естественно.

Вердикт: Лучший выбор для приложений, которым нужны админ-панели, управление контентом или быстрое прототипирование с «батарейками в комплекте».

Flask

Flask — минималистичный вариант. Он дает маршрутизацию и обработку запросов и ничего больше. Каждая дополнительная функция — это решение и библиотека, которую вы подключаете.

Скорость разработки: Flask стартовал медленнее всех, потому что каждая функция требовала отдельного решения. SQLAlchemy для ORM, Flask-JWT-Extended для аутентификации, Flask-Migrate для миграций. Каждая требовала настройки и имела свои соглашения.

Производительность под нагрузкой:

  • Среднее время ответа: 201 мс.
  • P95: 389 мс.
  • Запросов в секунду: 261.
  • Доля ошибок: 0,4%.

Немного медленнее Django под нагрузкой. Flask тоже работает синхронно, и накладные расходы экосистемы расширений накапливаются при параллельных запросах.

Сложность кода: Гибкость, которая делает Flask привлекательным, оборачивается несогласованностью. Если над проектом на Flask работают четыре разработчика, часто получается четыре разных паттерна для одной и той же операции.

# Flask - более явный, но и более многословный
@app.route("/orders")
def order_list():
    page = request.args.get("page", 1, type=int)
    orders = Order.query.options(
        joinedload(Order.user),
        subqueryload(Order.items)
    ).paginate(page=page, per_page=20)
    return jsonify({"orders": [o.to_dict() for o in orders.items]})

Где возникли сложности: Фоновые задачи потребовали той же настройки Celery, что и в Django. В ядре Flask нет нативной поддержки асинхронности.

Вердикт: Хорош для маленьких API и проектов, где вам нужен полный контроль над каждой зависимостью. Поддерживать его становится сложнее при масштабировании из-за несогласованности в кодовой базе.

FastAPI

FastAPI построен на Starlette и использует подсказки типов из Python для автоматической валидации и генерации документации. Это фреймворк, который я использую в большинстве своих продакшен-проектов.

Скорость разработки: FastAPI оказался на уровне Django по скорости старта. Модели Pydantic автоматически обрабатывают валидацию, а автоматически генерируемая документация по адресу /docs избавляет от отдельного этапа документирования.

Производительность под нагрузкой:

  • Среднее время ответа: 94 мс.
  • P95: 187 мс.
  • Запросов в секунду: 891.
  • Доля ошибок: 0,1%

FastAPI был значительно быстрее Django и Flask под параллельной нагрузкой. Асинхронная архитектура означает, что воркеры обрабатывают несколько запросов одновременно, пока ждут ответа от базы данных.

Сложность кода: Подход на основе подсказок типов сохраняет код чистым и самодокументируемым.

# FastAPI - подсказки типов делают всю тяжелую работу
@app.get("/orders", response_model=PaginatedOrders)
async def order_list(page: int = 1, db: AsyncSession = Depends(get_db)):
    result = await db.execute(
        select(Order).options(selectinload(Order.items))
        .offset((page - 1) * 20).limit(20)
    )
    return {"orders": result.scalars().all()}

Где возникли сложности: Нет встроенной админ-панели. Аутентификация требует ручной реализации или подключения библиотеки. Асинхронная модель требует понимания асинхронного Python, иначе производительность страдает.

Вердикт: Лучший выбор для высоконагруженных API, микросервисов и любых приложений, где важно время ответа при параллельной нагрузке.

Starlette

Этот фреймворк меня удивил.

Starlette — это ASGI-фреймворк, на котором построен FastAPI. Большинство Python-разработчиков знают о его существовании, но немногие используют его напрямую. FastAPI добавляет валидацию через подсказки типов, автоматическую документацию и внедрение зависимостей поверх Starlette. Использовать Starlette напрямую — значит писать все это самому.

Я включил его, ожидая, что он будет непрактичен для реальных приложений. Я ошибался.

Скорость разработки: Медленнее, чем у FastAPI, потому что нет автоматической валидации или документации. Каждый эндпоинт требует явного разбора запроса и построения ответа.

Производительность под нагрузкой:

  • Среднее время ответа: 71 мс.
  • P95: 134 мс.
  • Запросов в секунду: 1247.
  • Доля ошибок: 0,05%

Starlette оказался на 40% быстрее FastAPI и более чем в 4 раза быстрее Django. Без слоя валидации и накладных расходов на внедрение зависимостей запросы проходят через стек с минимальной обработкой.

# Starlette - явно, но чрезвычайно быстро
async def order_list(request):
    page = int(request.query_params.get("page", 1))
    async with AsyncSession(engine) as db:
        result = await db.execute(
            select(Order).offset((page - 1) * 20).limit(20)
        )
        orders = result.scalars().all()
    return JSONResponse({"orders": [o.__dict__ for o in orders]})

Где возникли сложности: Отсутствие валидации означает, что ошибки валидации становятся ошибками времени выполнения, а не автоматическими ответами 422. Нет внедрения зависимостей — общая логика требует явной передачи или глобального состояния. Чтобы создать продакшен-приложение на Starlette, нужно перестроить те компоненты, которые FastAPI предоставляет бесплатно.

Вердикт: Правильный выбор для приложений с очень высокой пропускной способностью, где важна каждая миллисекунда, и вы готовы сами реализовать валидацию и документацию. Не практичен для большинства команд.

Сравнительные результаты

Django. Среднее время ответа: 187 мс. Запросов в секунду: 284. Скорость разработки: самая быстрая. Подходит для полноценных приложений и админ-панелей.

Flask. Среднее время ответа: 201 мс. Запросов в секунду: 261. Скорость разработки: самая медленная. Подходит для небольших API и полного контроля.

FastAPI. Среднее время ответа: 94 мс. Запросов в секунду: 891. Скорость разработки: быстрая. Подходит для высоконагруженных API и микросервисов.

Starlette. Среднее время ответа: 71 мс. Запросов в секунду: 1247. Скорость разработки: медленная. Подходит для максимальной пропускной способности и производительности.

Что именно меня удивило

Я предполагал, что Starlette будет непрактичным, но бенчмарки изменили мое мнение.

Для некоторых случаев использования, особенно высоконагруженных микросервисов, где опытная команда готова взять на себя дополнительный шаблонный код ради лучшей производительности, Starlette — сильный вариант. Выигрыш в скорости на 40% по сравнению с FastAPI значителен при масштабировании.

Меня удивила цена в виде времени разработки. Эндпоинт регистрации, который занял 25 минут в FastAPI, потребовал 90 минут в Starlette, потому что валидацию и обработку ошибок пришлось писать с нуля.

Этот компромисс имеет смысл только тогда, когда выигрыш в производительности приводит к реальной экономии на инфраструктуре.

Какой фреймворк вам стоит использовать

Для большинства приложений сегодня FastAPI — правильный выбор. Преимущество в производительности над Django и Flask значительно, а опыт разработки лучше, чем у Starlette.

Используйте Django, если вам нужна админ-панель, встроенная аутентификация или готовый фреймворк с «батарейками» для контентно-ориентированного приложения.

Используйте Flask, если ваша команда обладает глубокой экспертизой во Flask, а приложение достаточно мало, чтобы проблема несогласованности не имела значения.

Используйте Starlette, если вы создаете высоконагруженный микросервис, и ваша команда достаточно опытна, чтобы построить слой валидации без структуры, которую дает FastAPI.


Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи inprogrammer: I Compared 4 Python Web Frameworks. One Surprised Me Completely

Предыдущая статьяUltrathink: Claude Code меняет правила разработки ПО