вторник, 13 сентября 2011 г.

Python vs Ruby в Web

Являясь хоть и человеком не с огромным опытом за плечами, однако интересующийся web-технологиями, и работающим в этой области, то хотелось высказать свое мнение, в ответ на частый спор, или "холивор": Python vs Ruby. И в частности, я беру область именно веб-программирования и рынок веб-фреймворков. Здесь, я не хочу разглагольствовать, принимать чью-либо сторону из-за "православных" взглядов. Я просто хочу высказать свою точку зрения, и прошу не призывать меня к "холивору".
Тем, кому все таки моя точка зрения интересна, то прошу под кат.

Итак. Python и Ruby. Для меня это действительно две жемчужины из языков программирования. Я ни в коем случае, не умоляю красоты и мощи других языков, но просто эта парочка мне очень нравится.


И ввиду моего интереса к этим языкам, и ввиду моей специализации на веб-программировании, я очень часто встречаюсь с различными холиворами на эту тему.

Python очень хорошо показал себя, как мощный язык, на котором могут быть написаны очень сложные приложения, и веб-сервисы. Ну чего далеко ходить. Его для своих нужд широко использует Google, Facebook, Яндекс. Но стоит оговориться. Эти компании используют Python отнюдь не только для написания веб-приложений, но и для внутренних утилит и серверов.

Ruby почти неизвестен за пределами веб-программирования, и во многом он обязан этим Ruby On Rails. Этот фреймворк с момента появления до сих пор даже не собирается спускаться с пьедестала и купается в лучах славы. Но должен сказать, что на нем написан далеко не один веб-фреймворк, и компании использующие его далеко не малы, и очень известны.

WSGI - из Python в Ruby

Python пришел на арену веб-программирования раньше Ruby, да и многих других людей, и коммьюнити развивающее язык предложило очень мощную концепцию WSGI, которая стала основополагающей для разработки веб-приложений на Python.
Концепция оказалась весьма удачной. Ее в той или иной форме заимствовали в разные языки, и одним из них является Ruby. Рубисты знают WSGI как Rack. Да-да. Это не что иное, как реализация WSGI-протокола, пришеднего из Python.

Теперь посмотрим на то, что получилось. Скажу сразу, что питонисты просчитались, и просчитались очень сильно. WSGI был выпущен как протокол, как стандарт, но увы. Никто не догадался, что хорошо бы сделать мощную реализацию этого протокола, поверх которой можно было бы строить веб-приложения. Но библиотеки таки появились. Django реализует свою поддержку WSGI, Armin Ronacher написал всем известный Werkzeug, Ian Bicking написал WebOb вошедший в Python Paste утилиты. Последние две библиотеки действительно мощные, спору нет.

Рубисты пошли другим путем. Christian Neukirchen написал Rack, который стал канонической реализацией WSGI для Ruby, и который используется во всех веб-приложениях, написанных на Ruby. При этом, он учел все слабые места WSGI, и очень хорошо адаптировал протокол для Ruby.

В итоге мы получили, что в Python появился стандарт, который не меняется годами, разработчики вынуждены выбирать одну из WSGI-реализаций (да, в стандартной библиотеке реализация есть, но я буду удивлен, если ей кто-нибудь пользуется), и хотели бы, или нет, но библиотеки могут только помочь облегчить участь работы WSGI, но не исправить его слабые стороны.

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

Но, пару лет назад, вышел Python 3.0, и тут началась карусель с WSGI 2.0. И как я понял, там сейчас далеко не все хорошо.

Инфраструктура запуска приложений

Теперь перейду к сравнению того, как WSGI и Rack позволили развить инфраструктуру запуска приложений.

Что мы имеем в Python? mod_python, mod_wsgi для Apache и nginx. При этом, для nginx он почти умер. Веб-серверов на Python почти нет (Tornado в данном случае не беру, это скорее веб-фреймворк для написания асинхронных приложений в виде веб-сервера, нежели веб-сервер в нормальном понимании).

Что мы имеем в Ruby? Rack-совместимые Mongrel, Webrick, Unicorn и многие другие. Старый способ запуска Ruby-приложений - nginx/ligthttpd в виде прокси на mongrel-cluster или Unicorn. А потом появился магический Phusion Passenger (аля mod_rails, mod_rack). Да. Не спорю, он был ориентирован, да и остается ориентирован на Rails. Однако, вспомним, что Rack - это каноническая реализация WSGI на Ruby, и единственная. И вуаля. Вместо плагина для Apache/nginx мы получили удобный и мощный, производительный инструмент для запуска не только Rails, но и любого Rack-приложения. При этом, обе версии развиваются активно, и очень хороши.

Обзор веб-фреймворков

Теперь посмотрим на веб-фреймворки.

На обоих языках было написано огромное количество различных веб-фреймворков, как больших, так и микро.

На Python, самыми популярными, и заслуживающими внимания на данный момент являются: Django, Pylons, Pyramid, Flask, repoze.bfg.
На Ruby: Ruby On Rails, Sinatra, Merb.

Стоит сразу уточнить, что после долгого времени разработки, разработка Pylons была прекращена после выпуска версии 1.0, и вылился в разработку Pyramid, как слияние Pylons и repoze.bfg.

Django - в свое время, этот фреймворк произвел фурор. Он очень удобный, в нем куча батареек, он на удобнейшем Python, у него все есть, и даже само организующаяся админка.

Pylons - мощный, и главное очень гибкий веб-фреймворк, который попытался вобрать в себя многое как из Django, repoze.bfg так и из Ruby On Rails.

repoze.bfg - мощный WSGI-фреймворк, написанный авторами Zope, и многое оттуда заимствующий. Очень мощный, очень сложный, и был очень крутой, но сомневаюсь, что широко известный.

Pyramid - результат слияния repoze.bfg и Pylons. Считается, что он должен быть очень мощным, и очень крутым. На самом деле, как и предок BFG многое наследует из Zope.

Flask - микро-фреймворк, написанный Armin Ronacher в честь первоапрельской шутки, и в итоге ставший очень популярным инструментом.

Ruby On Rails - его надо представлять? Ребята из 37Signals создали его для своих нужд, но он оказался весьма и весьма удачным. На данный момент он дорос до версии 3.1, изменил в себе многое, дал кучу идей, обрел кучу завистников и поклонников, ну и вообще он крут.

Sinatra - да. Это старший брат Flask, написанный на Ruby. Маленький, быстрый, простой, и очень популярный, при этом, активно пользуется инфраструктурой Rails, а порой и внутри Rails.

Merb - младший брат Rails. Появился как аналог Rails 2.0, обладал гораздо большей скоростью, и легковесностью. Стоит отметить, что команды Rails и Merb объединились для создания Rails 3.

Раунд 1 - Django vs Ruby On Rails 3.1

Итак. Взглянем на эти два фреймворка. Что Django может предложить, чего не может предложить Ruby On Rails?

У Django есть свой шаблонизатор, который кстати многих достал в свое время, и часто его заменяли на Jinja2, Mako, Genshi и другие. Но увы. Это не так легко, и требует написания дополнительного кода.

Что мы имеем в Ruby On Rails? По умолчанию, там используется eRuby, который в принципе является аналогом Django шаблонизатора. Однако, помимо него, можно прикрутить любой понравившийся шаблонизатор, без каких-либо проблем вообще. Более того. Их легко можно комбинировать в одном файле.

Еще одним камнем преткновения будет HAML. Это очень удобный, и очень хороший HTML-шаблонизатор, у которого нет(!) аналогов на Python. Да. Есть две библиотеки, одна из которых компилирует HAML в Django-шаблоны, а другой использует HAML-подобный язык для компиляции в шаблоны Jinja2. Но они куда как далеки от самого HAML.

UPD: Аналоги на Python для HAML есть, даже несколько, но как их красиво и удобно с меньшими затратами использовать для Django лично я не нашел. Но думаю попытки однозначно были.

Еще один камень в огород Django. Как заменить стандартный шаблонизатор на другой? Да. Надо копаться, и настраивать его.

Как такое провернуть в RoR? Просто прописываем в Gemfile зависимость для плагина, и все. Как правило все сконфигурировано. При этом, могут быть использованы шаблоны хоть десяти шаблонизаторов одновременно, и все будет корректно обрабатываться.

Django ORM - еще один камень преткновения. Многие знают, что ей куда как далеко до той же SQLAlchemy. Но заменить ее - вряд ли получится. Можно конечно, попробовать использовать. Но при этом придется столкнуться с многими проблемами, которые не нужны. Ну или искать свой backend.

Как это делается в Rails? При генерации приложения указываете не использовать ActiveRecord, а потом подобно с щаблонизаторами, ставите нужную ORM. Все. Больше от вас ничего не потребуется. Как правило, интерфейс всех ORM на Ruby пытается быть похожим на ActiveRecord, или имеет такой интерфейс, что позволяет огромному количеству дополнений работать одинаково без изменений как на ActiveRecord, так и на других. Да и в любом случае. Написать backend для своего расширения, для работы с конкретной ORM там куда легче, чем тоже проделать с Django. Да и выбор ORM на Ruby куда богаче. Как минимум три мощных библиотеки ActiveRecord, DataMapper и Sequel реализующие различные паттерны, против SQLAlchemy, ну и может Zope DB (а ее кто-то использует вне Zope?). Других аналогов на Python, которые могли бы потягаться с любой из Ruby-ORM я просто не знаю.

Ну и конкретные примеры. Что есть у ActiveRecord из коробки? Миграции, динамические запросы, полиморфные связи, виртуальные поля моделей(о да, привет паттерны проектирования, аля Active Record), динамические запросы.

Что из этого есть у Django ORM? Да нифтеига там нет, грубо говоря. Миграции - ставь South, динамические запросы - ну еще как то, виртуальные поля - ну костылями реализуемо, полиморфные связи - забудьте.

А расширяемость? Извините, но тут вступает в игру метапрограммирование. Тут реально уже, против истины не попрешь, и Ruby с инструментами метапрограммирования рвет в расширяемости.

Но по удобству использования любая Ruby ORM рвет Python'овские как Тузик грелку. Тут увы, но удачное и красивое применение возможностей "магии" (которая на самом деле и не магия, да и вполне легко понятные приемы, если почитать Metaprogramming Ruby).

Что еще. Ах. Как же я забыл. Админка Django. Она кажется неизменна с хрен знает каких бородатых версий. И это была гордость Django. Что ж. У Ruby On Rails есть rails_admin. Хотя честно признаться. Возможности Rails позволяют легко и не утруждая себя написать административную панель даже вручную.

Расширяемость. У Django расширяемость гораздо слабее. В Rails благодаря опять таки возможностям Ruby, и правильной архитектуре можно легко не только написать плагин, но и написать расширение для любой части Rails, или вашего кода.

Вообще, должен сказать, что когда пишешь приложение на Ruby On Rails, просто теряется граница где язык, а где начинается Rails. Ибо, в ActiveSupport очень много расширений для самого Ruby.

MVT против MVC. Честно. Раньше, я не особо понимал, разницы и слабых сторон подхода MVT. Сейчас они мне кажутся очевидными. Вот зачем разработчикам Django было изобретать колесо? Все ведь вернулось обратно. В Django появились вьюхи как классы, что очень похоже на контроллеры или ресурсы, но увы. Этого мало.

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

Разделение в Django на приложения казалось удобным. Но в Rails такое можно обойти плагинами, или просто папками и модулями, раскидывая по ним контроллеры, модели и статику.

А REST на Django. Лучшей реализацией является django-piston, который к тому же, уже год как не развивается совсем. Да и по сравнению со стандартным Rails - это убожество.

Ради чего вся эта свобода в Django? Только много ручной работы, и никакого удобства.

Стоит сказать еще пару слов о подходе "соглашений", которое широко используется в Rails. Этот подход позволяет достичь высокой гибкости, при этом строго ограничивая структуру приложения, и непрягая обязательными условиями. Он ненавязчиво подсказывает, как делать лучше и правильнее, и архитектура приложения выстраивается сама очень элегантно.

Кстати, это камень в огород не только Django, но и многих Java-фреймворков. А вы как думали, почему Play! Framework и Grails так легко завоевали Java-разработчиков?

Что касается общей инфраструктуры, то жалкие батарейки Django становятся просто ионами, на фоне электростанций Ruby On Rails. Правда. Сравните Django Packages c к примеру Ruby Toolbox или Rails Plugins. Вы все поймете.

Так. Подойдем к тестированию. Чем тестируют Django-приложения? Да. Обычными UnitTest'ами. Но какой бы крутой фреймворк для тестирования на Python вы не выбрали бы, RSpec и Cucumber просто похоронят их заживо. Кроме того, подход BDD реально проверен и удобен при разработке веб-приложений. А эти инструменты делают его не просто работоспособным, но и приносящим удовольствие. Да чего там. Просто автоматизируете тестирование, и при сохранении любого файла проекта, видите результат запускаемых тестов в области уведомлений вашего DE, причем вы только сохраняли файл и не больше.

A Rake? У Python такого просто нет. Увы. Но даже близко. А вещь очень интересная, и очень удобная. Не ну есть конечно Python Paste, но по сравнению с Rake он слабоват, но главное, он никоим образом не связан с Django. Такие дела.

На последок пару слов. В среде Rails появились и популяризировались такие вещи как CoffeeScript, Compass, Less. И если, кто-то скажет, что эти вещи - пустой звук, то я буду знать, какую квалификацию ему присвоить как разумному веб-разработчику.

Еще пару камней. Теперь в Rails 3.1 есть HTTP Streaming, Assets из коробки, CoffeeScript из коробки, SASS из коробки. У Django есть такие батарейки? Сомневаюсь.

А шаблоны приложений? Да. Для тех, кто видел только Django - то, вот, можете глянуть на такую вот вещь: Rails Wizard.

Думаю достаточно и этого, чтобы вы поняли, насколько Ruby On Rails превосходит Django. И это далеко не шутки. Отрыв между ними уже давно перерос в пропасть. А Django-разработчиков, со слабыми нервами, я думаю итак уже почти убил.

Раунд 2 - Pyramid vs Rails

По этой части сказать сильно не могу. Pylons в свое время понравились, а сейчас думаю понравились бы еще больше, после знакомства с Rails. Однако, и там все далеко не безупречно и красиво. А с приходом Pyramid, мне честно говоря, знакомиться с этим не хочется. repoze.bfg развивают очень мощные и классные идеи, очень красивые и наследующие мощь Zope. Однако, они очень сложны в первоначальном понимании, и не очевидны на первый взгляд. И думается, что несмотря на наследие и стремления, удобство Pyramid вряд ли переплюнет удобство Rails, а по скорости разработки - тем более.

Раунд 3 - Flask vs Sinatra

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

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

Flask же, очень хорошо зарекомендовал себя своими подходами, да и Werkzeug и Jinja в backend'е все таки доставляют. Он реально удобен, в меру мал, хорошо документирован и удобен.

Единственным слабым местом, мне кажется, стоит назвать то, что инфраструктура, которая вокруг него собрана, просто малютка, по сравнению с Sinatra. И в данном случае, эта причина не связана напрямую с тем или иным языком или фреймворком. Это всего лишь хвост кометы, под названием Ruby On Rails.

Подведу черту уже...

Пост получился просто разгромный. Кто-то точно подумает, что я нахваливаю Ruby. Но в свою защиту скажу. Опыт программирования на Python у меня больше, чем на Ruby. И работаю я сейчас Django-разработчиком, при этом Django пару лет не трогал, а последнее время возился с Rails. И поэтому, я сейчас очень остро ощущаю разницу, вынужденность много писать на Django, бороться с рутинными задачами, и прочим. Это реально коробит, но это и реалии положения на сегодняшний день.

Rails придя на рынок, устремил за собой поток разработчиков, собрав их в единый и красивый поток, чего нет в Python, хотя коммьюнити куда как более сильное и сплоченное. К сожалению, мне весьма и весьма кажется, что выбраться из этой ямы Python'у в области веб-программирования если и получится, то с огромным трудом, и не без помощи очень высоких затрат на разработку. Но пока все делается, так как делается, вряд ли, что-то сможет измениться. Пока будут выпускать стандарты WSGI, вместо реализации, пока будут плодить десятки фреймворков с батарейками, и изобретать MVT-велосипеды - никакого продвижения не будет.

Ситуация очень похожа на HTML. Так вот. HTML вплоть до 5-ой версии - это все Python WSGI, Django, Pyramid, Pylons и прочее. Четкие стандарты, велосипеды и наследие прошлого. Да это путь Python. Но веб-программирование - это очень динамичная область, требующая огромной гибкости, и скорости реакции. И как  показала практика, это так и есть, и современный подход Python с его консервативностью - далек от тех реалий, которые действительно требуются.

А вот Rails и Ruby в данном случае выступили как HTML5. Они плюнули на подготовки стандартов. Они сказали - наши стандарты живут и развиваются с нами. Нужно новое что-то? Ок. Мы это реализуем, и будем использовать, и это станет нашим стандартом. Когда что-то нужно - это просто делается и используется, вместо нескольких лет непонятных согласований, за которое все это протухает. Это реально живое сообщество, быстро революционирующее.

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

Вот как то так, получилось. Длинно, мудренно, восхваляюще и обвиняюще. Помидорами не кидаться, но замечания приму. =)