Почему я перестал использовать ORM в новых проектах

2026-08-14 · заметка

Первый проект на ORM я поднял в 2019-м и был доволен: модели описаны один раз, миграции едут сами, код читается. Проблемы начались, когда запросов стало больше пятнадцати на экран и половина из них перестала помещаться в простые фильтры.

Что произошло

Сложную выборку всё равно приходится писать руками. То нужны оконные функции, то CTE, то специфичный join с условием, которое ORM выражает настолько громоздко, что проще спуститься на уровень SQL. В итоге в проекте сосуществуют два стиля: простые операции через ORM и всё интересное через сырые запросы. Поддерживать это труднее, чем один последовательный подход.

Вторая проблема — отладка. Когда запрос тормозит, нужно понимать, какой SQL ушёл в базу. С ORM это лишний шаг: включи логирование, найди сгенерированный текст, сопоставь его с кодом на Python или Go. С рукописным SQL текст запроса лежит прямо в коде, и EXPLAIN ANALYZE применяется к нему без перевода.

ORM экономит время на прототипе и стоит времени на поддержке. Вопрос только в том, какой этап для вас длиннее.

Что делаю теперь

  • Простой CRUD и прототипы — ORM остаётся, она здесь действительно быстрее.
  • Боевые сервисы — сырые запросы плюс тонкая обёртка для сканирования строк.
  • Миграции — отдельный инструмент, не привязанный к маппингу объектов.
  • Каждый запрос в коде сопровождается комментарием, зачем он нужен и какой индекс использует.

На Go для этого хорошо подходит sqlx или pgx напрямую, на Python — asyncpg без прослойки. Код получается длиннее, но каждая строка в нём означает ровно то, что произойдёт в базе.

Пример

Выборка последних событий по каждому пользователю. Через ORM это попытка выразить оконную функцию средствами QuerySet. Напрямую — один читаемый запрос:

SELECT DISTINCT ON (user_id) user_id, created_at, event_type
FROM events
WHERE created_at > now() - interval '7 days'
ORDER BY user_id, created_at DESC;

Плюс частичный индекс (user_id, created_at DESC) с условием по дате — и запрос идёт за миллисекунды на миллионах строк.