В прошлом году я принял решение, что не буду больше использовать Rails, и не буду поддерживать Rails в своих гемах. Кроме того, я буду делать все возможное, чтобы мне никогда не пришлось снова столкнуться с Rails на работе. Так как я вовлечён во множество Ruby-проектов, люди часто спрашивают меня, почему я не люблю Rails, какие проблемы у меня есть с ним и так далее. Поэтому я решил написать этот длинный пост, чтобы подвести итоги и все объяснить. Статья частично техническая, частично личная и, к сожалению, частично гневная. Я пишу это не для того, чтобы привлечь внимание, получить посетителей и т.п., у меня нет никакого интереса в этом. Я пишу это, потому что я хочу закончить мои дискуссии о Rails и чтобы было место, куда давать ссылки каждый раз, когда я слышу одни и те же вопросы. Я также хотел бы рассказать вам пару историй, которые "начинающие Rails-разработчики", вероятно, никогда не слышали, и осветить некоторые вопросы, которые являются достаточно важными, чтобы по крайней мере, подумать о них. Я не буду делать вид, что все в Rails плохо, неправильно, и фреймворк является воплощением зла. Это было бы не справедливо, да и просто глупо. О Rails можно сказать очень много хорошего. И я упомяну пару (очевидных?) вещей для поддержания баланса. Итак, Rails сделал Ruby популярным. Это факт. Я стал Ruby-разработчиком, что в свою очередь изменило мою карьеру и дало мне много удивительных возможностей, именно благодаря Rails. Многие рубисты тех дней прошли по тому же пути. Мы все здесь благодаря Rails. Во многих случаях, Rails на самом деле оказал огромное влияние на людей, и буквально улучшил им жизнь. Люди получили лучшие рабочие места, лучшие возможности и хорошие деньги. Это было радикальной сменой условий "игры" для многих из нас. На протяжении многих лет Rails & DHH вдохновили многих людей, в том числе людей из других сообществ, на переосмысление того, что они делают. Например, я уверен, что Rails способствовало улучшению PHP сообщества (вы можете попытаться доказать, что я ошибаюсь, но у меня есть довольно яркие воспоминания как Symfony подчерпнул кучу вдохновения от Rails). То же самое произошло в Java, фреймворк Play является примером. Есть и другие фантастические аспекты. Rails был всегда нацелен на простоту использования и возможность быстро создавать веб-приложения, что позволило преуспеть таким инициативам, как Rails Girls. Rails доказал людям, что они способны создать что-то самостоятельно, без какого-либо опыта программирования, в относительно короткий промежуток времени. Это удивительно, так как это легко может стать воротами в мир программирования для людей, которые в противном случае даже не рассматривали возможность стать программистом. Позвольте мне рассказать вам немного о моем бэкграунде и как я пришёл к Rails. Я начал работать с Ruby в конце 2006 года, поскольку моя дипломная работа была о Rails. Я изучал язык в то время, как писал свой диплом. Это было весело, это было интересно, это было что-то новое для меня. Тогда я еще работал в качестве PHP-разработчика. И как типичный PHP-программист в ~2005-6, я делал все эти типичные вещи: писал SQL-запросы в шаблонах представлений, писал в процедурном стиле, а затем разрабатывал свой собственный фреймворк и свой собственный ORM, разочаровывался и выгорал. Несмотря на некоторые знания C, C++, Java и Python, я решил перейти на Ruby, из-за Rails. Я выбрал их для своего диплома и совершенно случайно наткнулся на предложение о работе от местного магазина на Rails. Я ответил и они наняли меня. Это было в марте 2007 года. Итак, с марта 2007 года я начал работать с Ruby профессионально, и где-то с 2009-го я начал вносить вклад в OpenSource проекты на Ruby. С тех пор я работал в консалтинговой компании в течение 3.5 лет, в основном c большими и сложными проектами. Потом работал фрилансером в течение нескольких лет, работал с кучей клиентов, открыл свою собственную компанию, а затем вернулся на полный рабочий день, а затем снова на фриланс, и теперь я вновь штатный сотрудник. Я создавал Rails-приложения с нуля, и участвовал в разработке относительно больших Rails-приложений. Позвольте мне рассказать вам одну историю. Однажды я присоединился к существующему проекту. Это было огрооомное приложение, которое управляло веб-сайтом торгового он-лайн сообщества. Сложные модели продаж, сложные промо-акции, затейливые конфигурации товаров, купоны, группы пользователей, сообщения — в нём было всё это. Я присоединился к ним, чтобы помочь добавить несколько новых фич. Одной из моих первых задач было… добавить ссылку на что-то на какой-то странице. Мне потребовалось несколько дней, чтобы добавить эту глупую ссылку. Почему? Приложение было большим шаром сложной логики предметной области, раскатанным по нескольким слоям и с настолько сложными вьюхами, что было не просто даже найти нужный шаблон, в котором добавить ссылку. Так как мне нужны были некоторые данные из заказа для того, чтобы создать эту ссылку, не было очевидно, как я должен получить их. Недостаток внутренних API приложения и опора исключительно на ActiveRecord сделали эту задачу чрезвычайно трудной. Я не шучу. Мои первые фрустрации с Rails начались довольно рано. Я испытал недовольство от ActiveRecord примерно после 6 месяцев его использования. Также мне никогда не нравилось, как Rails подошел к работе с JavaScript и AJAX. В случае, если вы не помните, или не застали версии до того, как Rails принял UJS подход (который был хитом обсуждений в 2007-2008 годах), Rails использовал inline JavaScript в шаблонах, порожденный связкой противных хелперов. Как и всё с Rails, это было "легко и приятно в начале", а затем превращалось в неподдерживаемое дерьмо. В конце концов, Rails принял UJS в версии 3.0 и, кажется, сообщество согласилось, что это лучший подход. Это было, когда Merb был убит влит в Rails. О, вы не знаете, что такое Merb? Хорошо, давайте поговорим об этом. Merb — проект, созданный Ezra Zygmuntowicz. Он начался как хак, чтобы сделать загрузку файлов быстрее и потокобезопасной. И он прошёл интересный путь от этого хака до модульного, потокобезопасного, быстрого full-stack фреймворка для разработки веб-приложений. Я помню, как люди начали говорить о Merb в 2008-м и это было удивительное чувство, что происходит что-то новое, и это будет здорово! Вы возможно обрадовались, когда в Rails появился режим API, не так ли? Хм, а Merb имел 3 режима: режим полного стека, режим API и микро-режим, в котором он был урезан до минимума, и я до сих пор помню, что это было самой быстрой вещью когда-либо существовавшей в Ruby мире. Это было более 7 лет назад. Вдумайтесь в это. В то же время еще один проект привлёк внимание сообщества — DataMapper. Он стал частью Merb стека, являясь его выбором для ORM. Я был очень возбуждён тем, как он решал многие проблемы, которые были в ActiveRecord. DataMapper уже в ~2008-9 имел определения атрибутов в моделях, пользовательские типы, ленивые запросы, более мощный DSL запросов и так далее. В 2008 году Yehuda Katz был одним из Core-разработчиков проекта, он активно продвигал его и был большой ажиотаж по этому поводу. DataMapper в конечном счете был лучшим ORM, чем ActiveRecord в 2008-9. Было бы несправедливо не упомянуть о том, что Sequel появился примерно в то же время и до сих пор он используется гораздо меньше, чем ActiveRecord, несмотря на то, что является более хорошим решением. Я был восхищён Merb и DataMapper так, как они принесли надежду, что мы можем сделать что-то лучше и создать здоровую конкуренцию для Rails. Я был взволнован, потому что оба проекта поощряли модульный подход и потокобезопасность, не говоря уже о таких вещах, как просто более хорошие стандарты написания кода. Оба проекта были в конечном счете убиты Rails, так как Merb был "влит" в Rails, что вылилось в огромный рефакторинг Rails для версии 3.0. DataMapper потерял внимание сообщества и без особой поддержки проект не смог развиваться, как это могло бы быть, если Merb не был бы "влит" в Rails. С этим решением экосистема Ruby потеряла кучу важных проектов и только Rails выиграли от этого. Является ли решение убить Merb хорошим или нет, это вопрос личного мнения, мы можем только предполагать, что могло бы произойти, если бы решение не было принято. Тем не менее, есть простая истина о конкуренции — это здорово. Отсутствие конкуренции означает монополию, и есть простая истина о монополии — это не здорово. Конкуренция способствует прогрессу и инновациям, конкуренция создает здоровую экосистему, она позволяет людям сотрудничать больше, чтобы поделиться тем, что распространено, чтобы создавать лучший фундамент. Это не то, что происходит в сообществе Ruby. После того, как Merb & DataMapper были практически уничтожены (в долгосрочной перспективе), создать нечто новое в экосистеме Ruby оказалось крайне сложно. Поскольку внимание людей сфокусировано на Rails, новые проекты были под его сильным влиянием. Прорваться с новыми идеями было трудно, если не сказать больше… так как каждый раз, когда вы что-то демонстрируете, люди просто хотят, чтобы это было похоже на Rails и хорошо работало с Rails. Делать проекты, которые работают с Rails, — трудно, но я вернусь к этому вопросу позже. После всех этих лет мы в конечном итоге пришли к одному фреймворку, подчиняющему всю нашу экосистему, влияя на тысячи разработчиков и создание стандартов, которые… весьма спорны. Мы потеряли разнообразие экосистемы, которая начала расти в 2008 году и была подавлена Rails. Эй, я знаю, как это звучит почти как теории заговора, но не относитесь к этому так. То, что я сказал здесь, — это факты с примесью моих личных чувств. Я начал участвовать в DataMapper в конце 2009 года и, видеть как он рушится было очень грустно. Сложность является нашим самым большим врагом. Люди стали проявлять меньше энтузиазма к Rails, поскольку быстро оказалось, что имея дело с растущей сложностью, он оставляет нас с большим количеством вопросов без ответа. То, что предлагают DHH & co. никогда не было достаточно, чтобы решить многие проблемы, с которыми тысячи разработчиков начали бороться ещё в 2007-2008 гг. Некоторые люди надеялись, что, возможно, Merb/DataMapper принесут улучшения, но теперь вы знаете, что произошло, так что мы все вернулись обратно к Rails в 2010 году, когда Rails 3.0 был выпущен. Пару дней назад кто-то запостил на /r/ruby ссылку на статью об организации кода с помощью "Service объектов". Это одна из многих подобных статей. Если вы думаете, что это своего рода тенденция последнего времеми, взгляните на статью James Golick от марта 2010 — "Crazy, Heretical, and Awesome: The Way I Write Rails Apps". Мы говорим о путях совершенствования архитектуры наших Rails приложений в течение примерно 6 лет. Я пытался внести свой вклад в эту дискуссию насколько я мог — статьями, выступлениями на конференциях и работой над многими проектами с открытыми исходниками, которые стремятся решать различные сложные задачи. Однако подобные аргументы и идеи всегда высмеиваются членами Rails Core Team, и особенно DHH. Это было отпугивающим и обескураживающим для меня, и это причина почему я никогда не пытался внести свой вклад в Rails. Я чертовски уверен, что мои предложения будут в конечном итоге заминусованы. Monkey-patches? Да ладно, не проблема, мы любим нашу 10.years.ago! Новые абстракции? Кому это нужно, Rails — это просто! TDD? Не проблема, она мертва, не беспокойтесь! ActiveRecord раздутая — ну и что, это же так удобно, давайте добавим больше возможностей! Экосистема Rails, особенно вокруг своей Core Team, никогда не производила на меня хорошее впечатление, и для меня не проблема признать, что я просто боюсь предлагать любые изменения. Это особенно актуально, так как первое, что я хотел бы предложить "Пожалуйста, удалите ActiveSupport" (ха-ха… представьте себе это!). Ладно, давайте перейдем к некоторым техническим деталям. Как я уже говорил, Rails был построен с прицелом на удобство использования. Не путайте это с простотой. Только вчера я наткнулся на этот твит, который говорит об этом всё:  Вот как работает Rails, классический пример: Вы видите простую строку кода, и вы можете сразу сказать(если вы знаете, является ли User моделью ActiveRecord), что она делает. Проблема здесь заключается в том, что люди путают простоту с удобством. Эту строку удобно/легко написать в вашем контролере и всё сразу заработает, не так ли? Однако, эта строка кода не является простой, её легко написать, но код "под капотом" чрезвычайно сложен, так как: Это нехватка разделения ответственности, которая всегда наносит ущерб любому сложному проекту. Это увеличивает связывание(coupling) и затрудняет изменение и расширение кода. Но в Rails мире это не является проблемой. В Rails мире основные принципы дизайна такие как SRP (и SOLID в целом) высмеивают и представляют как "раздутые, ненужные абстракции, увеличивающие сложность". Когда вы говорите, что предпочитаете моделировать варианты использования приложения, используя свои собственные объекты и делать сложные части явными, лидеры Rails скажут вам YAGNI. Когда вы говорите, что вы предпочитаете использовать композицию, которая делает ваш код более надежным и гибким, лидеры Rails, за исключением tenderlove, скажут вам “use ActiveSupport::Concerns”. Для Rails-разработчика не проблема, что данные, поступающие из веб-формы направляются в глубины ActiveRecord, где Бог знает, что произойдет. Действительно сложная часть в этой дискуссии — быть в состоянии объяснить, что это первоочередная проблема. Людей привлекает Rails, потому что он дает вам ложное чувство простоты, в то время как то, что происходит на самом деле, заключается в том, что сложность спрятана за удобными интерфейсами. Эти интерфейсы основаны на многих предположениях о том, как вы собираетесь писать и проектировать ваше приложение. ActiveRecord является лишь одним типичным примером, но Rails полностью построен на этой философии, каждая часть Rails работает аналогично. Я должен упомянуть, что я знаю об огромных усилиях сделать ActiveRecord лучше, таких как введение Attributes API (сделано с помощью серьезного внутренного рефакторинга, который улучшил кодовую базу). К сожалению, до тех пор пока ActiveRecord поставляется с более чем 200 публичными методами и поощряет использование колбеков и concerns, это всегда будет ORM, которая не в состоянии справиться с растущей сложностью. Изменится ли эта философия в Rails? Я так не думаю. У нас нет никаких признаков того, что что-то улучшится в этом отношении, так как лидеры Rails просто против этого. Простое доказательство — недавнее спорное дополнение ActiveRecord.suppress, которое было предложено самим DHH. Обратите внимание на то, как он в очередной раз подшучивает над обычными Ruby классами, говоря: "Да, вы можете добиться того же, создав отдельную фабрику CreateCommentWithNotificationsFactory". Ну-ну! Должен ли Rails являться вашим приложением? Это был важный вопрос, который задают многие после просмотра выступления дядюшки Боба, где он в основном предлагает более сильное разделение между веб-частью и вашим основным приложением. Отбросив технические детали, это хороший совет, но Rails не был разработан с учетом этого факта. Если вы делаете это с Rails, вы теряете всю суть этого фреймворка. По факту, взгляните на то, что сказал DHH об этом:  Его мысли на этот счёт предельно ясны, не так ли? Важной частью является "конечно, является". И знаете, что? Я полностью согласен! Rails является вашим приложением, и всегда будет так, покуда вы не прилагаете огромные усилия, чтобы использовать его таким образом, для которого этот фреймворк не был предназначен. Подумайте об этом: Имея это в виду, было бы сумасшествием считать, что Rails — не ваше приложение. Если вы пытаетесь избежать этого типа связанности, можно себе представить, какие усилии придётся приложить и сколько из встроенной функциональности вы потеряете. И именно поэтому демонстрация альтернативных подходов в Rails создают впечатление раздутых, ненужных абстракций, напоминающих людям об их "страшных" Java днях. Rails не был построен с ориентацией на низкую связность и компонентно-ориентированный дизайн. Не боритесь с этим. Примите это. Несмотря на всё вышесказанное, моя самая большая жалоба на Rails — это ActiveSupport. Так как я уже писал об этом, я не чувствую, что мне нужно повторяться. Я также рекомендую почитать комментарии в той статье. Единственное, что я хотел бы добавить, что именно из-за ActiveSupport я не считаю Rails желанной частью экосистемы Ruby. Эта библиотека является квинтэссенцией всего неправильного в Rails для меня. Отсутствие фактических решений, отсутствие чётких абстракций, просто куча воркэраундов для решения проблемы под рукой, воркэраунды, которые превращены в официальный API, и карго-культ в результате. Чудесно! Rails является закрытой экосистемой, и это накладывает плохие требования на другие проекты. Если вы хотите заставить что-то работать с Rails, вы должны позаботиться о таких вещах, как работоспособность при подключенном ActiveSupport, или что ничего не сломается от зверской перезагрузки кода в режиме разработки, или что объекты предоставляются глобально, потому что, ну вы знаете, в Rails всё должно быть доступно везде, для вашего удобства. То, как работает Rails, требует много дополнительных усилий от разработчиков, создающих свои гемы. Прежде всего, следует ожидать, что ваши гемы могут работать с Rails (потому что, очевидно, все будут использовать их с Rails), и это само по себе является непростой задачей. У вас есть библиотека, которая работает с базами данных, и вы хотите заставить её работать с Rails? Ну вот, теперь вы должны заставить её работать, как ActiveRecord, более или менее, поскольку интерфейс интеграции — ActiveModel, изначально основан на ActiveRecord времён Rails 3.0. Есть множество ограничений, которые делают интеграцию с Rails весьма трудной задачей. Вы понятия не имеете, со сколькими проблемами вы можете столкнуться при попытке заставить код работать с горячей перезагрузкой кода в режиме разработки. Rails ожидает глобальную, изменяемую среду выполнения. Для того, чтобы сделать всё еще труднее, они ввели Spring. Этот гем открывает целую категорию потенциальных новых багов, с которыми ваши гемы могут столкнуться, когда вы пытаетесь заставить их работать с Rails. Я настолько задолбался с этим, друзья мои, что мне больше нечего сказать. Мало того, что перегрузка кода в Ruby работает ненадежно, так это к тому же добавляет кучу совершенно ненужной сложности для наших гемов и приложений. Это влияет на всех, кто разрабатывает гемы, которые должны работать с Rails. Никто из команды ядра Rails, несмотря на критику на протяжении многих лет, даже не думал, что было бы хорошей идеей, посмотреть, как это можно было бы сделать лучше. Если бы кто-нибудь просто сосредоточился на повышении скорости загрузки приложения, мы могли бы положиться просто на перезапуск процесса. Кроме того, вы должны действительно использовать автоматизированное тестирование, чтобы удостовериться, что изменение, которое вы только что сделали, на самом деле работает, а не жать F5. Просто к вашему сведению. Я знаю, это звучит, как будто я жалуюсь, хотя по сути так и есть! Я пытался поддерживать Rails и это было слишком разочаровывающим опытом для меня. Я сдался, я не хочу делать это больше. Посколько моё решение проблем подразумевало выдирание ActiveSupport, удаление ActiveRecord и добавление нормального слоя представления, который был бы отделен от любого ORM, я понял, что неразумно думать, что это когда-нибудь произойдет в Rails. В результате девяти грёбанных лет работы с Rails и активной работы над многими OpenSource проектами на Ruby, я сдался. Я больше не верю, что нечто хорошее может случиться с Rails. Это моя личная точка зрения, но многие люди разделяют те же самые чувства. В то же время, есть много других, которые по-прежнему счастливы с Rails. Рад за них! Честно! Rails вошёл во всеобщее употребление, у него есть своя сфера применения, он по-прежнему помогает людям и это зрелый, хорошо поддерживаемый, стабильный веб-фреймворк. Я не пытаюсь убедить кого-либо, что Rails в конечном счете плохой! Он просто очень плохой для меня. Впрочем это имело свои последствия. Именно поэтому я оказался вовлечён в такие проекты, как dry-rb, hanami and trailblazer и поэтому я давно работаю над rom-rb. Я хочу помочь построить новую экосистему, которая, мы надеемся, вернёт тот же самый энтузиазм, который мы все чувствовали во времена Merb и DataMapper. Нам нужна разнообразная экосистема, нам нужно больше маленьких, целенаправленных, простых и надёжных библиотек. Нам нужны рубисты, которые чувствуют себя комфортно, используя фреймворки совместно с небольшими библиотеками. Правда в том, что уход от Rails — также начало моего следующего путешествия — ухода от Ruby в качестве основного языка. Я вдохновился функциональным программированием в последние пару лет. Вы можете видеть, что в этом стиле я пишу теперь и на Ruby. Я наблюдаю за ростом Elixir с большим воодушевлением. Я также изучаю Clojure, который в данный момент находится на вершине моего списка "языки для изучения". Чем больше я узнаю, тем больше я люблю его. Моя конечная цель состоит в том, чтобы изучить ещё и Haskell, поскольку я заинтригован его статической типизацией. В настоящее время на работе я программирую на Scala. Я смог очень быстро оценить преимущества статической типизации в нём, несмотря на то, что это была жёсткая перестройка моего рабочего процесса разработки, включая компиляцию и TypeError ошибки. Это освежающий опыт — видеть как мой редактор говорит мне, что я сделал ошибку, прежде чем я добрался до выполнения каких-либо тестов. Чем больше я погружаюсь в функциональное программирование, тем больше я вижу насколько отстаёт Rails, когда дело доходит до современного дизайна приложений. Monkey-patching, опора на глобальное изменяемое состояние, сложный ORM — эти вещи рассматриваются в качестве основных проблем в функциональных языках. Я знаю, многие скажут "но Ruby является объектно-ориентированным языком, используй это в свою пользу, вместо того, чтобы пытаться сделать из него то, чем он не может быть". Но это не так. Прежде всего, Ruby является объектно-ориентированным язык с функциональными особенностями (блоки, лямбды и т.д.). Во-вторых, избегание изменяемого состояния является общим, хорошим советом, который вы можете применить в своем Ruby-коде. Устранение глобального состояния и изолирование его, когда вы не можете его избежать, — также очень хороший общий совет. Как бы там ни было, я покидаю Ruby. Я уже начал этот процесс. Он займёт годы, но это мой путь. Я буду продолжать работать и поддерживать rom-rb, dry-rb, помогать с hanami и trailblazer. Так что не волнуйтесь, эти проекты очень важны для меня, и я счастлив наблюдать рост их сообществ. Это заранее подготовленный список обратной связи и вопросов, но он основан на реальной обратной связи, которую я получал. Это является наиболее распространенным вариантом обратной связи, которую я получаю. Прежде всего, меня беспокоит, что многие люди так реагируют. Мы обсуждаем инструмент, а не человека, нет необходимости воспринимать это так эмоционально. Не поймите меня неправильно, я понимаю, что это естественно "защищать" что-то, что помогло вам, или то, что вам просто нравится, в то же время здорово иногда выходить за рамки привычного мышления, быть открытым к критике и спокойно анализировать её. Если вы счастливы с Rails, это здорово, на самом деле. Этот тип обратной связи раньше меня очень злил и огорчал. В момент написания этой статьи, в соответствии с GitHub, я сделал 2435 коммитов в OSS прошлом году. Это было в моё свободное время. Да, я не способствовал улучшению непосредственно Rails из-за причин, которые я объяснил в этой статье. Там слишком много того, с чем я не согласен, и это было бы пустой тратой времени для обеих сторон. Я вносил свой вклад через статьи в блогах, доклады на конференциях и тысячи строк OpenSource кода, которые вы можете найти на GitHub. Это абсолютно мимо кассы. Нам необходимо разнообразие в экосистеме с хорошим выбором библиотек, а также несколько фреймворков с их уникальным набором фич, делающим их пригодными для различных вариантов использования. Форк Rails не будет иметь никакого смысла. Никто не будет форкать Rails, чтобы пройти через такие испытания, как удаление ActiveSupport и устранение высокой связности от таких концептов как ActiveRecord и т.д. Проще и быстрее создать что-то новое, что другие люди уже и делают (см. Hanami). Системный архитектор