Python или Go: две задачи и условия выбора — это разбор двух учебных сценариев. Задача A — регулярная еженедельная очистка sales.csv силами одного сопровождающего. Задача B — HTTP API на четыре разработчика с пиком до 300 req/s. Язык выбирается по навыкам команды, контрактам и ограничениям.
Задача A: регулярная недельная очистка CSV
Задача A — это регулярная еженедельная очистка sales.csv. Полный рабочий объём по ТЗ: около 2 млн строк, 12 колонок. Один сопровождающий уверенно знает Python, срок первой версии — 2 дня. Цель — report.csv с нормализованной датой и суммой без дублей order_id. Учебный лимит времени прогона — 10 минут, памяти — 2 ГиБ; достижение этих значений не заявляется без проверки.
Двенадцать полей входа: order_id, paid_at, amount, currency, customer, sku, qty, region, channel, status, source, note. Малый вход для ручной приёмки содержит четыре строки, и две из них — дубликат O1. Это и есть проверочный случай: дедупликация по order_id должна оставить ровно три заказа.
order_id,paid_at,amount,currency,customer,sku,qty,region,channel,status,source,note
O1,12.11.2026,120.00,RUB,C1,SKU1,1,RU,web,paid,manual,a
O1,12.11.2026,120.00,RUB,C1,SKU1,1,RU,web,paid,manual,a
O2,2026-11-12,80.00,RUB,C2,SKU2,2,RU,web,paid,manual,b
O3,13.11.2026,40.00,RUB,C3,SKU3,1,RU,email,paid,manual,c
Ожидаемый report.csv — две строки: 2026-11-12 с двумя заказами и суммой 200.00, затем 2026-11-13 с одним заказом и суммой 40.00.
date,orders,total_rub
2026-11-12,2,200.00
2026-11-13,1,40.00
Первым кандидатом для A идёт Python по имеющимся навыкам и сроку. Лозунг о CPU-параллелизме здесь не работает: задача упирается в разбор текста и агрегацию. Полноразмерный прогон проверяет заданные 10 минут и 2 ГиБ отдельно от малого входа.
Задача B: шесть endpoints и пик 300 req/s
Задача B — HTTP API на четыре разработчика: трое поддерживают Python async, один знаком с Go. Согласованное сопровождение двух языков пока отсутствует. Пик 300 req/s и 90% ожидания I/O — учебная предпосылка.
| Endpoint / пик req/s | Вход/выход и зависимость | Базовый кандидат | Возможная граница Go/Python |
|---|---|---|---|
| GET /healthz / 1 | Без БД, 200 с status=ok | Python API | Go front |
| GET /metrics / 1 | Текстовые счётчики, без ML | Python API | Go front |
| GET /v1/orders/{id} / 180 | id=O1; БД → order_id/status/amount | Python async API | Go front + та же БД |
| POST /v1/orders / 60 | customer=C1, sku=SKU1, qty=1 → order_id; запись БД | Python async API | Go front + та же БД |
| POST /v1/quote / 50 | sku=SKU1, qty=1 → quote_id/amount; внешнее API | Python async API | Go front + тот же контракт |
| POST /v1/predict / 8 | model=risk_v1, features=[0.1,0.2,0.3,0.4] → model и score от 0 до 1; Python-модель | Python API с контролем CPU-части | Go front проксирует predict-контракт Python-обработчику |
Сумма нагрузок: 1+1+180+60+50+8=300. Основная доля приходится на чтение заказа по id — 180 req/s, и это I/O-операция с обращением к БД. ML-зависимость сосредоточена в одном endpoint: POST /v1/predict обслуживает Python-модель risk_v1 и требует отдельно выбранного ограничения или изоляции CPU-части, чтобы не блокировать весь event loop.
Границы владения различаются. Базовый кандидат — единый Python API: текущая команда его поддерживает, а asyncio подходит I/O-bound коду. Возможная согласованная граница — Go front, который проксирует согласованный predict-контракт Python-обработчику; ML-библиотека в Go автоматически не переносится. Каждый endpoint получает владельца, и этот владелец фиксируется до выбора языка.
Ограничения: p95, память, стоимость
Ограничения задаются как учебные условия, и достигнутые результаты не выдумываются. SLO для не-ML маршрутов — p95 ≤300 мс. Для POST /v1/predict — p95 ≤1500 мс, поскольку вызов модели идёт дольше обычного запроса. Доля ошибок — менее 1%.
Ресурсы API вместе с моделью — не более 2 vCPU и 2 ГиБ. Потолок инфраструктуры — 10 000 ₽/мес; это условие заказчика. Ни один из этих показателей не подтверждён измерением: они образуют договор, по которому сравниваются кандидаты.
| Параметр | Значение | Статус |
|---|---|---|
| p95, не-ML маршруты | ≤300 мс | учебное ограничение |
| p95, POST /v1/predict | ≤1500 мс | учебное ограничение |
| Доля ошибок | <1% | учебное ограничение |
| Ресурсы API + модели | ≤2 vCPU / 2 ГиБ | учебное ограничение |
| Потолок инфраструктуры | 10 000 ₽/мес | условие заказчика |
| Пик нагрузки | 300 req/s | учебная предпосылка |
Порядок работы с ограничениями такой. Сначала фиксируются значения и единицы измерения. Затем для каждого кандидата прогоняется один и тот же сценарий. После прогона записываются p95, ошибки, память и цена своей инфраструктуры. Решение принимается по записи.
Python async как исходный кандидат
Исходный кандидат для B — Python async. Документация Python описывает asyncio как библиотеку для конкурентного кода с синтаксисом async/await и отмечает, что она подходит для I/O-bound и сетевого кода (docs.python.org). Это совпадает с профилем задачи: 90% ожидания — I/O.
CPU-инференс требует отдельного решения. Документация не обещает, что asyncio ускоряет CPU-часть; модель в POST /v1/predict нужно ограничивать или изолировать. Это отдельный пункт договора.
Go и hybrid — условная альтернатива. Go-слой рассматривают, если есть поддержка второго стека и измеренный профиль или деплойные требования оправдывают границу. Пик 300 req/s сам по себе этого не доказывает: сумма нагрузок не показывает, где именно теряется время. Hybrid допускается только при подтверждённом выигрыше по обязательному ограничению, поддержке двух стеков и бюджете.
Практический порядок выбора: назвать владельца каждого endpoint, проверить, что команда сопровождает выбранный стек, и только затем добавлять второй язык. Трое из четырёх разработчиков поддерживают Python async, один знаком с Go — этого достаточно для исходного кандидата и недостаточно для постоянного сопровождения двух стеков.
GIL и сборки: чего нельзя обобщать
Утверждение «Python всегда с GIL» неверно. Документация Python описывает free-threaded сборку: начиная с версии 3.13 CPython поддерживает сборку, в которой global interpreter lock отключён (docs.python.org). Проверить режим можно через python -VV, sys.version и функцию sys._is_gil_enabled(); переменная sysconfig.get_config_var(«Py_GIL_DISABLED») показывает, поддерживает ли сборка free threading.
Отдельные расширения могут вернуть GIL. Документация предупреждает: сторонние пакеты с extension-модулем могут быть не готовы к free-threaded сборке и включают GIL повторно, о чём печатается предупреждение. Совместимость расширений и реальный режим проверяются отдельно для конкретного набора зависимостей.
Free-threaded сборка имеет свою цену. Документация приводит средние накладные расходы на одном потоке от примерно 1% на macOS aarch64 до 8% на x86-64 Linux, а также увеличенный расход памяти: часть объектов становится immortal, не-GC объекты получают больший заголовок, аллокатор mimalloc откладывает возврат памяти операционной системе.
Go тоже требует проверки. В Go команда go build компилирует пакеты вместе с зависимостями и создаёт executable, а go install компилирует и устанавливает пакеты (go.dev). Executable не означает отсутствия зависимостей: архитектура, CGO, сертификаты, модельные файлы и контейнерные зависимости проверяются отдельно. Goroutines поддерживают конкурентное исполнение и мультиплексируются на потоки ОС, но нужный p95 они не гарантируют.
Две приёмки: логика CSV и нагрузочный договор
Приёмка первая — логика CSV. На малом входе после дедупликации по order_id должно остаться ровно три заказа, суммы 200.00 и 40.00, даты нормализованы к виду 2026-11-12 и 2026-11-13. Регулярный полный файл проверяется отдельно на заданные 10 минут и 2 ГиБ. Эта проверка не зависит от языка: она проверяет результат.
Приёмка вторая — нагрузочный договор. Для обоих архитектурных кандидатов проводится один и тот же сценарий по шести строкам endpoints. Записываются p95, доля ошибок, память, цена своей инфраструктуры и наличие владельца каждого endpoint. Сценарий одинаков для Python и для hybrid, иначе сравнение теряет смысл.
Порядок действий по второй приёмке:
- Зафиксировать шесть endpoints и их пиковые значения.
- Задать SLO, память и потолок стоимости как условия.
- Прогнать одинаковый сценарий для каждого кандидата.
- Записать p95, ошибки, память и цену.
- Назначить владельца каждого endpoint.
- Сравнить записи с условиями договора.
Ручной эталон решения: Python-кандидат остаётся, если выдерживает весь договор. Hybrid допускается только при подтверждённом выигрыше по обязательному ограничению, поддержке двух стеков и бюджете. Ни одного такого результата редакция не измеряла — это учебный разбор.
Частые вопросы
Python или Go — что выбрать для регулярной очистки CSV?
Для задачи A первым кандидатом идёт Python: сопровождающий уверенно знает язык, срок первой версии — 2 дня, а работа упирается в разбор текста и агрегацию. Полный файл на 2 млн строк и 12 колонок проверяется отдельно на 10 минут и 2 ГиБ. По заданным навыкам и сроку Python остаётся исходным выбором; преимущество смены стека требует отдельной проверки.
Хватит ли Python для API с пиком 300 req/s?
Исходный кандидат для B — единый Python API, потому что трое из четырёх разработчиков поддерживают Python async, а 90% ожидания — I/O. Пик 300 req/s складывается из шести endpoints, и основная доля — чтение заказа по id. Выдержит ли кандидат договор, показывает одинаковый нагрузочный прогон.
Когда стоит добавить Go-слой?
Go-слой рассматривают, если есть поддержка второго стека и измеренный профиль или деплойные требования оправдывают границу. Hybrid допускается при подтверждённом выигрыше по обязательному ограничению, поддержке двух стеков и бюджете. Пик нагрузки сам по себе границу не доказывает.
Правда ли, что Python всегда ограничен GIL?
Нет. Начиная с версии 3.13 CPython поддерживает free-threaded сборку с отключённым GIL, а проверить режим можно через python -VV, sys.version и sys._is_gil_enabled(). Отдельные расширения могут вернуть GIL повторно, поэтому совместимость зависимостей проверяется отдельно. У free-threaded сборки есть накладные расходы и увеличенный расход памяти.
Достаточно ли go build, чтобы получить самодостаточный файл?
go build компилирует пакеты с зависимостями и создаёт executable, go install компилирует и устанавливает пакеты. Executable не означает отсутствия зависимостей: архитектура, CGO, сертификаты, модельные файлы и контейнерные зависимости проверяются отдельно. Goroutines поддерживают конкурентное исполнение, но нужный p95 не гарантируют.
