Вопросы авторам курса «Разработка на Ruby on Rails»

Авторы самых интересных технических вопросов по Rails получат скидку 5 000 рублей.

На страницу курса
Виктор Соколов
Виктор Соколов
Злой, марсианский антикризисный тимлид. Ведет разработку проектов для eBay. Контрибутил в CarrierWave и Hanami, написал sidekiq-grouping. В свободное от работы время учится летать на самолете.
Сергей Пономарёв
Сергей Пономарёв
Тимлид в Злых Марсианах. Писал API-only бэкенд тогда, когда это ещё не было мейнстримом.
Владимир Кочнев
Владимир Кочнев
Злой марсианин из российской провинции. Интересуется функциональным программированием и системами типов. Контрибьютил в Celluloid и Grape.
Владимир Дементьев
Владимир Дементьев
Математик‑программист, с недавних пор обитающий на Марсе. В свободное время коммитит в Rubocop и Ruby on Rails, а в свободное от кода время играет в мяч и на различных музыкальных инструментах.
Алексей Газиев
Алексей Газиев
Технический директор Амплифера, тимлид в проектах Марсиан. Написал gon, talks, ids_please. Отлаживал и коммитил в Rails, Ruby, Sidekiq. Не умеет учить сёрфить и ездить на мотоцикле.
Андрей Дерябин
Андрей Дерябин
Дослужил на Марсе за пять лет до сана тимлида. Помогал делать Groupon, Рокетбанк и Gett. Коммитер в Hanami.
Андрей Ситник
Андрей Ситник
Ведущий фронт‑эндер Злых Марсиан. Автор PostCSS и Автопрефиксера. Работал над Groupon, Амплифером и Рокетбанком. Постоянный спикер российских и западных фронт‑энд конференций.
Как лучше организовать структуру проекта, который имеет одно ядро (модели), но предоставляет несколько разных интерфейсов. Например, билинговая система с интерфейсом для менеджеров, личным кабинетом для клиентов, платежным шлюзом для колбеков платежных систем. Каждый из этих интерфейсов располагается на разных доменах. Для каждого из интерфейсов используется своя система авторизации. Часть шаблонов и ассеты у личного кабинета и интерфейса менеджеров пересекаются. Рендер страниц делается на сервере, JS используется для «оживления» страниц.
Алексей Газиев
Алексей Газиев

На ум приходят три варианта, как это можно реализовать.

Первый и не совсем подходящий вариант – разбить такое приложение на три раздельных приложения в духе SOA. В данном случае это будет проблематично, потому что SOA рассчитана на независимые куски, взаимодействующие друг с другом посредством сообщений. В вашей постановке задачи чистого разбиения нету – придется выносить общую часть в гем или дублировать код. Второе сразу отметается, а первое – приведет к проблемам поддержки кода моделей (и других часто меняющихся компонентов).

Второй вариант – разбить единственное приложение территориально, выделив, например, неймспейсы для определенных контекстов в нужных областях приложения. Например, в app/controllers добавить неймспейсы, отвечающие за взаимодействие с менеджерами, клиентами и другими сущностями по отдельности. Но общие модели в этом случае все равно будут аккумулировать код для разных аспектов приложения, усложняя их поддержку. В этом случае хорошо разделить ответственность по работе с моделями с помощью DCI. Данные (минимальные модели) будут общими, контексты для работы с ними будут либо общими, либо частными для конкретного интерфейса, также как и взаимодействия для описания работы с контекстами (главное, используйте для реализации контекстов декораторы, а не concerns). В мире Rails составляющие DCI имеют разные другие названия (UseCases, Services, Entities, etc), но смысл не меняется.

Третий вариант, который мы и рекомендуем, дублирует второй, но разбиение происходит с использованием Engines вместо неймспейсов в рамках одного приложения.

P.S.

Для разделения бизнес‑логики от внешних зависимостей (в том числе и веб‑интерфейсов), есть принципы гексагональной и чистой архитектуры, но они имеют большой оверхед для данной задачи. Так как бизнес‑логика абстрагируется не только от интерфейсов, но и баз данных, систем очередей и других зависимостей. Для задачи подходит упрощенный частный случай.

Почему в rails принято подключать application.js в head (так сделано, например, на этом сайте)? Не правильнее ли подключать весь js (кроме, библиотек подобных google-analytics) в конце страницы, чтобы как можно быстрее загрузить DOM ?
Андрей Ситник
Андрей Ситник

Да, практика размещения JS-скриптов <head> — очень странная. Но дело в том, что перенос скриптов внизу страницы — тоже не идеальное решение, загрузка JS-файлов начнётся позже.

Есть более правильный подход, которое имеет все плюсы обоих решений. Если вы можете отказаться от поддержки IE 9 и более ранних версий, то гораздо лучше оставлять <script> в <head>, но добавить к скрипту атрибут defer (так сейчас сделано на сайте Брейнвошинга). Он говорит браузеру запустить скрипт после загрузки всего HTML (то есть равносилен $(document).ready). К сожалению, в IE до версии 10 была ошибка реализации этот атрибута.

Хотя для IE 9 тоже можно предложить решение — убирать defer если по HTTP_USER_AGENT мы видим, что пришёл IE с ошибкой реализации атрибута.

Как вы относитесь к тому, что рельсы сейчас могут являться просто источником данных для javascript'а на фронтенде? Не перейдет ли вся веб разработка к одностраничным сайтам?
Андрей Ситник
Андрей Ситник

Не перейдёт ;).

Для этого вопроса я бы разделил сайты на две категории — веб‑страницы (куда вы приходите за данными, например, Википедия) и веб‑приложения (где вы данные создайте, например, веб‑версия Фотошопа).

Веб‑приложениям нужна работа оффлайн, там очень сложные интерфейсы — логично, что рендер должен идти полностью на клиенте, сервер будет «тонким». Для таких задач подходят тяжёлые фреймворки типа Ember.js.

Но веб‑страницу хочется найти в поисковике, просто открыть ссылку и читать — без сложного интерфейса или ожидания инициализации JS. Для таким задач лучше использовать рендер на сервере, а JS делать «тонким», только оживляющим данные в HTML. Тут нужны более простые фреймворки — например, Evil Blocks или Twitter Flight.

Рынок веб‑приложений только появился и постоянно растёт. Он требует совсем новых инструментов, так что мы регулярно узнаём о интересных и сложных решениях, которые там появились. Но в итоге, читая только о сложных больших фреймворках, у нас складывается ошибочное мнение, что вся разработка состоит из одностраничных приложений.

К сожалению, люди гораздо чаще потребляют информацию, чем её создают. Большинство сайтов всё равно будут простыми и удобными страницами для чтения. Нам всё равно нужно будет читать обычные статьи в Википедии или на Хабрахабре. Промо‑страницы всё равно будут содержать статичный контент, который проще заранее отрендерить на сервере.

Привет! Расскажите, как у вас устроена работа с клиентскими библиотеками? Устанавливаете ли вы для них гемы, кладете их в vendors, или каким‑то образом ставите через bower в рельсовое приложение? Используете ли вы асинхронную подгрузку js, или всё грузится сразу? Если да, то что для этого используете?
Андрей Ситник
Андрей Ситник

Сторонние библиотеки мы начали подключать через Rails Assets — он генерирует гемы из bower-пакетов. Если библиотека старая и её нет в Bower, то кладём в /vendor. Туда же кладём все купленные шрифты.

Асинхронную загрузку не используем — по‑моему, того, как не надо грузить ассеты. Скорости загрузки постоянно растут и уже довольно большие, а вот ожидание во время запроса‑ответа (latency) довольно большое и не будет уменьшаться, так как ограничено скоростью света. Поэтому, чтобы сайт грузился быстро, нужно в первую очередь уменьшать кол‑во запросов, а не размер файлов.

Так что мы собираем все файлы в один (только jQuery и Font Awesome грузятся с CDN). На большинстве проектов у нас вообще все‑все файлы собираются в один application.js, но иногда (впрочем, довольно редко) оптимизация имеет смысл и мы делаем сборку файлов самых популярных страниц и вторую сборку для менее популярных.

Мы собираем файлы с помощью Rails Assets Pipeline. Проблему прекомпиляции мы решаем соглашением — в корне app/assets/stylesheets и app/assets/javascripts у нас только файлы сборок (которые мы подключаем уже в HTML) — они уже подключают в себя файлы из подпапок. В итоге, мы заранее можем определить, какие файлы должны быть в прекомпиляции и добавляем их автоматически. Пример настроек для автопрекомпиляции можете посмотреть в Evil Front.

Сейчас большая часть интересного в веб‑разработке происходит именно на фронтенде: JS-фреймворки, CSS-методологии и многое другое. Куда движется фронтенд? Чего ждать от него в будущем? Каким образом это отразится на Rails?
Андрей Ситник
Андрей Ситник

Интересный, но и довольно сложный вопрос. Предсказывать будущее — не самое благодарное дело. Все мы помним, как научные фантасты 20 века слишком увлеклись космосом и просмотрели появление ИТ. Поэтому я опишу только те темы, которые я сам изучаю с прицелом на недалёкое будущее.

Во‑первых, это постпроцессоры CSS — PostCSS и Rework. Препроцессоры (такие, как Sass, Stylus и LESS) сильно ограничены по возможностям и мы давно сделали на них всё, что позволяет их парадигма. Постпроцессоры имеют бо́льшую гибкость для нового класса инструментов (Автопрефиксер, полифилы Myth).

Во‑вторых, наконец‑то большинство компиляторов поддерживают карты кода (source map). Они серьёзно упрощают отладку фронта на продакшене, упрощают внедрение новых языков, компилируемых в JS, позволяют сильно упростить системы сборки ассетов (таких, как Sprockets), отказавшись от разных режимов разработки и продакшена. Но разработчикам предстоит ещё большая работа по сведению всех компонентов вместе — карты кода будут работать только если все шаги обработки поддерживают их и обновляют карты с предыдущего шага.

В JS главной темой станет ECMAScript 6 и его новые возможности (например, генераторы). Уже сейчас можно использовать синтаксический сахар из ES 6 с помощью препроцессоров JS.

Мне очень хочется верить, что мобильная разработка будет переходить в Веб. Все текущие проблемы веб‑приложений на телефонах уже были пару лет назад на больших компьютерах, но там они были успешно решены — сейчас на ноутбуке я чаще пользуюсь сайтами, чем приложениями. Впереди ещё много работы (разработать больше API, ускорить рендер), но я не вижу фундаментальных причин, которые мешают мобильному Вебу.

Мне немного грустно говорить о фронтенде в Rails — команда Sprockets потеряла былой запал и давно не была примером инноваций (поддержки карт кода так и нет). Я бы сказал, что переход сборки ассетов на node.js просто неминуем. Но переходить на Grunt или Gulp мне совсем не хочется — декларативный подход Sprockets и «Convention over configuration» мне кажутся гораздо лучше архитектуры Makefile. Broccoli и Mincer мне нравятся гораздо больше.

Приветствую! Уже который раз натыкаюсь на мнение SEO'шников, что заголовок H1 должен быть на странице в одном единственном экземпляре. Однако, если верить W3C, в черновике спецификации про элемент section (http://www.w3.org/html/wg/drafts/html/master/sections.html#headings-and-sections) с примерами указано, что допускается использование нескольких элементов H1 внутри тега <section>. Андрей, подскажите, как быть и кому верить? Спасибо.
Андрей Ситник
Андрей Ситник

Правы и SEO, и спецификация — просто они говорят про разные вещи.

Спецификация HTML говорит о смысле документа — у каждого раздела статьи может быть свой главный заголовок.

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

Хотя, большинство SEO и похоже на средневековую медицину (чистый эмпирический опыт, слабая доказательная база), но тут SEO-специалист прав, даже если не думать о поисковой выдаче. Анализировать вложенные article, main и section в поисках главного h1 — сложная задача. Если же на странице только один h1 с самым‑самым главным заголовком, то найти его гораздо проще. Разве главный смысл ИТ не в борьбе со сложность? ;)

Хочу использовать flexbox сегодня. Какие существуют подходы для поддержки этой технологии в отсталых браузерах, включая IE8?
Андрей Ситник
Андрей Ситник

Большую часть флексбокса можно использовать начиная с IE 10. Автопрефиксер позволит вам не думать о префиксах и разнице между спецификациями.

Но чтобы увеличить поддержку до IE 8 придётся чем‑то пожертвовать. Полифилов для последней версии спецификации нет. Да и JS-полифилы бы плачевно сказались на производительности — а как мы знаем старые IE обычно стоят именно на старых компьютерах.

Так что я вижу единственный путь, чтобы использовать флексбоксы и поддерживать IE 8 — деградация на старых IE. В лучшем духе Mobile First и т. п. — сверстайте сначала макет для IE 8 в виде одной колонки, пусть не такой удобной, но чтобы она позволяла решать бизнес‑задачи. А поверх этой просто вёрстку уже сделайте стили флексбока, которые бы делали сайт удобнее и приятнее.

У деградации под старыми IE есть большое преимущество перед полифилами — пока мы верстаем под всеми браузерами пиксель‑в-пиксель, люди не будут обновляться. Но если мы покажем им, что их любимые сайты в новых браузерах удобнее и приятнее, люди радостью поставят самую свежую версию браузера.

Какими архитектурными свойствами должно обладать приложение, чтобы можно было использовать Rails::Engines? Чего стоит избегать при проектировании такого приложения? С чего начать рефакторинг готового приложения в сторону модульной структуры?
Алексей Газиев
Алексей Газиев

Как и любая абстракция, engine применяется либо для использования в нескольких местах с похожим кодом, либо для упрощения текущего кода. Наилучший повод для использования engine – когда вам надо инкапсулировать все приложение или его часть для того, чтобы функционал этого приложения мог быть доступен в других приложениях. Таким образом, желательно, чтобы та часть приложения или само приложение, которое вы планируете вынести в engine, архитектурно позволяло использовать его где‑либо еще. Хорошие примеры использования engine — devise, active_admin и многие другие. По факту, множество гемов, имеющих отношение к рельсам, если не используют engine напрямую, то по крайней мере ведут себя как engine, добавляя вам в проект какую‑либо функциональность. Также, иногда разделение проекта на подпроекты используют для изолированной работы разных коллективов над разными частями проекта.

Избегать в первую очередь стоит использования инструмента не по назначению. То есть, само по себе разнесение на модули всего приложения без видимой практической пользы плюсов может не дать, а потенциальных минусов в виде усложнения работы над каким‑либо процессом «в целом» — легко. Во‑вторых, надо всегда помнить о том, что engine призван расширить основное приложение; что основное приложение должно всегда иметь приоритет и возможность переопределить части engine. В‑третьих, если вы используете engine типа full, думайте о нейминге, потому что данные engine встраиваются обычно без каких‑либо неймспейсов и возможны коллизии имен классов, роутов и тп.

Ну и о рефакторинге – как и было сказано выше, нужно думать о цели использования engine. Как пример: пусть у вас есть два приложения, в обоих реализован функционал блога. Можно задуматься над тем, чтоб вынести его в отдельный engine и просто подключить в оба приложения. Важно также подумать сразу о том, насколько может разойтись со временем модификация данного функционала в обоих приложениях и просчитать затраты на доработку гибкого поведения engine.

Задайте свой вопрос

Мы бережно сохранили ваш вопрос, скоро мы на него ответим.