Десять причин избегать тяжеловесных фреймворков, а также лишних зависимостей в проекте
27 мая 2015
Фреймворки бывают разные. Если, например, мы рассмотрим веб-фреймворки, то можно легко заметить их разделение на две большие группы — легковесные фреймворки (например, Scotty, Cowboy, Finagle) и тяжеловесные (Yesod, Play, Catalyst). Первые по сути предлагают собой встраиваемые веб-серверы, возможно, с поддержкой вебсокетов. Во вторых поверх всего этого еще накручена валидация форм, конфиги, i18n, ORM, и так далее. Оборачиваясь назад, я понимаю, что тяжеловесные фреймворки меня всегда чем-то беспокоили, но я не мог четко сформулировать, чем именно. Так было до недавнего времени.
На первый взгляд может показаться, что Scotty и Yesod предназначены для решения очень разных задач. На Yesod’е здорово писать обычные веб-приложения, «сайтики», с тоннами HTML, CSS, загрузкой картинок — вот этим всем. В то время, как Scotty предназначен скорее для написания REST-сервисов, общающихся с внешним миром исключительно при помощи JSON. Однако ничто же не мешает прикрутить к Scotty хождение в базы данных, какой-нибудь шаблонизатор, логирование, парсилку конфигов, и вперед, можно идти писать убийцу Facebook. Аналогичные рассуждения можно без труда применить и к другим фреймворкам, притом не только для веба.
Развивая эту мысль, можно быстро прийти к выводу о том, что тяжеловесные, фулстэк фреймворки вредны и во всем проигрывают легковесным фреймворкам. Вообще, последние обычно ближе к простым библиотекам, чем фреймворкам.
Смотрите сами:
- Несмотря на попытки создания модульных фулстэк веб-фреймворков, они все равно всегда тянут за собой 100500 зависимостей, многие из которых вообще ни для чего не нужны конкретно в вашем проекте. Зависимости эти долго вытягиваются, долго собираются (если в языке принято все собирать из сорсов), и так далее. Не забудьте добавить сюда конфликты зависимостей — cabal hell и подобные проблемы.
- Фулстэк фреймворки нередко оказываются излишне усложнены. Если вы пробовали прикручивать к Play плагин для аутентификации и авторизации, то понимаете, о чем я. Тот же Slick правильно подключить с первой попытки тоже довольно непросто, притом одним концом его нужно подключить к Play, а вторым — к JDBC. Зато модульно :(
- Казалось бы, последнее, что в наше время волнует программистов — это какого размера приложение получается на выходе. Если речь идет о разнице между 1 Мб и 10 Мб, то действительно, скорее всего, это не так уж важно. Однако в реальных условиях с тем же Play на выходе вы можете легко получить deb-пакет размером порядка 100 Мб. И это уже становится проблемой, так как заливка проекта в AWS по WiFi начинает занимать существенное время. Если клиенты сами ставят ваше приложение на своих машинах, они могут не дождаться загрузки или просто в испуге пойти к конкурентам.
- Тяжеловесные фреймворки нередко заставляют вас использовать кучу разных DSL под каждый отдельный случай. Конфиги пишем на одном языке, роуты задаем на втором, шаблоны на третьем, и так далее. Добавьте сюда еще всякие валидаторы форм, местные «более удобные» альтернативы CSS и JavaScript, описание схемы БД и миграций. Оглянуться не успеете, как начнете писать на десяти совершенно разных языках. В легковесных фреймоворках вас, конечно, тоже ждет мешанина из HTML, CSS, JavaScript и SQL, но эти языки вы хотя бы уже знаете и их все равно меньше.
- Опыт показывает, что, как ни крути, компиляция проекта будет занимать намного больше времени при использовании фулстэк фреймворков, чем при использовании легковесных фреймворков. За это время вы успеете отвлечься на болтовню или какое-нибудь чтение новостей, а вернувшись к проекту уже забудете, чем вы там занимались. Не стоит недооценивать важность скорости компиляции проекта. Она критически важна для производительности программиста.
- Нормальная поддержка крутого фулстэк фреймворка вашей любимой IDE может требовать приобретения платной версии этой самой IDE. Так в IntelliJ IDEA Community Edition довольно трудно работать с Play Framework. Поддержка этого фреймворка есть только в Ultimate Edition. А с легковесными фрейморками можно комфортно работать при использовании бесплатной версии IDE, а то и вовсе какого-нибудь Vim.
- Даже в платной IntelliJ IDEA поддержка Play, мягко говоря, оставляет желать лучшего. Заметные тормоза даже на очень хорошем железе, подсветка красным синтаксически верных конструкций, прочие всевозможные глюки — в общем, полный набор. Пользоваться очень неудобно.
- Использование фулстэк фреймворка, даже модульного, предполагает, что за вас выбрали некоторый набор стандартных решений. И решения эти могут быть не самыми удачными. Так в Yesod для работы с базами данных по умолчанию предлагается использовать Persistent. Этот ORM пытается быть настолько общим, что поддерживает как PostgreSQL, так и MongoDB. Поддерживает, понятно дело, одинаково плохо, так как это очень разные СУБД. И вы встаете перед выбором — использовать неудачные стандартные решения, или использовать нестандартные решения, которые (1) не описаны в книжке про фреймворк, по которой училась вся команда, и которые (2) не факт что хорошо впишутся в существующую инфраструктуру. В мире легковесных решений все модули независимы и взаимозаменяемы, поэтому такой проблемы не возникает в принципе.
- Чем больше кодовая база, тем больше в коде ошибок, тем сложнее поддержка кода и тем труднее в нем разобраться при возникновении проблем. Также куда больше сил тратится на поддержку документации. Вместо того, чтобы заниматься одной простой и понятной проблемой, разработчики фулстэк фреймворков разрываются между десятью.
- Нередко тяжеловесные фреймворки — это абстракции, построенные поверх абстракций, поверх абстракций. Потому есть веские основания полагать (сравните хотя бы Netty и Play в правой табличке), что тяжеловесные фреймворки ведут себя не лучшим образом в плане потребления памяти и CPU, времени обработки запросов, числа запросов в секунду и так далее. Очевидно, что при прочих равных Scotty делает намного меньше лишней работы, чем Yesod, а Spray — меньше, чем Play. Другими словами, фулстэк фреймворки более тормозные, чем легковесные решения. Если очень повезет — то почти такие же, но никогда не быстрее.
Как видите, у тяжеловесных фреймворков есть масса недостатков, и каких-то особых преимуществ при этом не наблюдается. Так зачем их использовать?
Несмотря на то, что выше речь шла о веб-фреймворках, те же рассуждения применимы и в других случаях. И даже если вы используете некий фреймворк, не нужно завязываться на все-все-все его фичи. Скажем, вы используете Akka. Вряд ли вам нужен Akka Cluster, очереди с приоритетами, роуты, стримы, FSM, и прочие возможности. Используйте минимальное требуемое подмножество фреймворка! В этом случае код будет намного проще, понятнее и быстрее собираться, будет меньше конфликтов зависимостей, будет проще перейти на следующую версию компилятора Scala, проще найти программистов, способных поддерживать проект, deb-пакеты будут заливаться в AWS по WiFi не по 15 минут, и так далее. Бывает еще и так, что разные программисты независимо друг от друга добавляют в проект разные зависимости для решения одной задачи. Я видел проект с тремя библиотеками для работы с JSON и двумя легковесными веб-серверами. Разумеется, это приводит к дублированию кода (всякие utils, кэши, тесты и прочее), необходимости понимать работу не двух библиотек, а пяти, трудностям при взаимодействии частей системы, использующих для одной задачи разные библиотеки, и массе других проблем.
Чем меньше зависимостей в проекте и тоньше фреймворки, тем лучше. Код должен быть простым. Даже если иногда это идет в ущерб лаконичности.
Метки: Разработка, Философия.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.