Компания
39,36
рейтинг
29 октября 2012 в 17:19

Разработка → Фронт-энд Островка изнутри

Привет, меня зовут Игорь (iamo0), я старший фронт-энд разработчик в Островке. Я занимаюсь нашим основным продуктом: сайтом Ostrovok.ru. С помощью нашего сайта ежедневно бронируют отели тысячи человек, поэтому для нас очень важно, чтобы качество нашего продукта было на высоте. А для этого нужно не отвлекаться на разного рода мелочи и уметь эффективно решать поставленные задачи.

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

Не претендую на то, что мой рассказ сорвет покровы или станет настоящим откровением. Хочу поделиться с вами опытом работы с большими приложениями, накопленным разработчиками Островка.

Командная работа

Автобусное число

В команде, занимающейся фронт-эндом Островка, работает девять человек. Как правило, даже двум разработчикам нелегко прийти к консенсусу, как правильно писать код (привет, Экстранет ;-), что уж говорить о девятерых. С другой стороны, нужно думать о том, чтобы архитектура проекта была ясной и четкой, код — легко читаемым и поддерживаемым. Важно, чтобы новичок, недавно пришедший в команду или человек, которому приходится переключиться на другой проект, смог быстро разобраться в коде.

У менеджеров и разработчиков есть термин «автобусное число». Это число разработчиков, котрых должен сбить автобус, чтобы проект стало невозможно поддерживать. В самом худшем случае автбусное число равно единице. Наша задача — максимально повысить автобусное число, в идеале, оно должно быть равным количеству разработчиков.

Высокое автобусное число легко достигается, если все разработчики пишут код в одинаковом стиле и знают что происходит в тех частях проекта, которыми они не занимаются.

Гайдлайны

Чтобы разработчики писали код в одинаковом стиле существуют гайдлайны. На одном из наших внутренних ресурсов любой разработчик может найти подробнейшие гайдланы по JavaScript, HTML и CSS, которые включают в себя не только правила форматирования кода, но и описывают особенности экосистемы нашего проекта: где и как мы храним файлы, каким образом организуем сборку проекта и некоторые правила написания кода.

Одним из важнейших правил, используемых в нашем проекте, является обязательное документирование кода с помощью jsDoc (http://code.google.com/p/jsdoc-toolkit/, https://developers.google.com/closure/compiler/docs/js-for-compiler). Использование jsDoc дает большие преимущества. Во-первых, возрастает читаемость кода. Во-вторых, во многих IDE невероятно удобно ориентироваться в проекте, в котором используется jsDoc. Те же, кто любит стрелять себе в ноги использует простые текстовые редакторы, вроде vim, могут скомпилировать документацию в HTML-файлы с помощью консольной утилиты идущей в jsDoc-toolkit’e. Ну и, наконец, чтобы правильно написать jsDoc, разработчик должен разложить все по полочкам у себя в голове, а это никогда не навредит.

Код-ревью

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

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

Особенности разработки

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

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

Попробуйте поменять фильтры на нашей поисковой выдаче, например количество звезд или стоимость номеров — список отелей обновится мгновенно, потому что все вычисления происходят на клиенте. Несмотря на то, что мы работаем с большими объемами данных — например, в Лондоне у нас больше двух тысяч отелей — сайт реагирует на запросы пользователя очень быстро.

JavaScript

Такой большой упор на клиентскую часть требует того, чтобы код был тщательно организован и структурирован. Мы избрали модульный подход к разработке, основанный на ООП. Это значит, что мы активно используем классы, наследования, слабое связывание и прочие прелести ООП, которые предоставляет JavaScript.

Для каждой страницы у нас есть отдельный JavaScript-класс, содержащий в себе какие-то вложенные модули, которые, в свою очередь, так же могут дробиться до бесконечности.

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

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

Мы организуем этот код так:

//base.js
/** 
 * @fileoverview Sperical code in vacuum for Habrahabr.ru
 * @author Igor Alexeenko (habrahabr.ru/users/iamo0)
 */

/** Project namespace */
var ota = {};

/**
 * Inheritance interface. Uses temporary constructor.
 * @param {Function} childCtor
 * @param {Function} parentCtor
 */
ota.inherit = function(childCtor, parentCtor) {
  var temporaryCtor = function() {};
  temporaryCtor.prototype = parentCtor.prototype;

  childCtor.prototype = new temporaryCtor;
  childCtor.prototype.constructor = childCtor;

  // Link to parent's prototype.
  childCtor._super = parentCtor.prototype;
};

/**
 * Base module. It's not necessary, to implement full base class in simple
 * example, so let it just be an empty class, inherited from Object.
 * @constructor
 * @extends {Object}
 */
ota.BaseModule = function() {};
ota.inherit(ota.BaseModule, Object);



//page.js
/** 
 * @fileoverview Page controller.
 * @author Igor Alexeenko (o0@ostrovok.ru)
 */

/**
 * @constructor
 * @extends {ota.BaseModule}
 */
ota.Page = function() {
  this._form = new ota.EmailForm;
};
ota.inherit(ota.Page, ota.BaseModule);



//searchform.js
/** 
 * @fileoverview Email form.
 * @author Igor Alexeenko (alexeenko.igor@gmail.com)
 */

/**
 * @constructor
 * @extends {ota.BaseModule}
 */
ota.EmailForm = function() {
  this._formElement = document.querySelector('.' + ota.EmailForm.CLASS_NAME);
  this._formElement.addEventListener(
      'submit', 
      ota.EmailForm.getEventHandler(this, this._onSubmit)
  );
};
ota.inherit(ota.EmailForm, ota.BaseModule);

/**
 * Class name, which uses for match DOM-element.
 * @const
 * @type {string}
 */
ota.EmailForm.CLASS_NAME = 'form-element';

/**
 * Error message, which displays if user, entered wrong email address.
 * @const
 * @type {string}
 */
ota.EmailForm.EMAIL_ERROR_MESSAGE = (
    'Введите, пожалуйста, правильный адрес электронной почты.'
);

/**
 * @const
 * @type {RegExp}
 */
ota.EmailForm.EMAIL_REGEXP = (
    /^[a-zA-Z0-9-_.+]{1,}@[a-zA-Z0-9-_.]{2,}\.[a-zA-Z]{2,}$/
);

/**
 * Static method. Returns event handler with certain context.
 * @param {Object} context
 * @param {Function} handler
 * @return {Function}
 */
ota.EmailForm.getEventHandler = function(context, handler) {
  return function(event) {
    handler.call(context, event);
  };
};

/**
 * Form submit handler. Alerts an error if email field is empty or its value
 * is invalid. Otherwise, submits the form.
 * @param {Event} event
 * @private
 */
ota.EmailForm.prototype._onSubmit = function(event) {
  event.preventDefault();

  var formIsValid = this.validateForm();
  var emailField = this._formElement.elements[0];

  if (!formIsValid) {
    alert(ota.EmailForm.EMAIL_ERROR_MESSAGE);
    emailField.focus();
    return;
  }
 
  this._formElement.submit();
};

/**
 * Form validator. Returns true if email field value is valid.
 * @return {boolean}
 */
ota.EmailForm.prototype.validateForm = function() {
  var emailField = this._formElement.elements[0];
  var emailValue = emailField.value;

  return (emailValue !== '' && ota.EmailForm.EMAIL_REGEXP.test(emailValue));
};


Теперь, чтобы заставить это работать, мне достаточно в HTML страницы поставить следующий код, разумеется, сначала подключив все необходимые файлы:

<script>
  new ota.Page;
</script>


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

Конечно, для решения такой небольшой задачи, кода получилось довольно много, но если представить, что на этой же странице есть поисковая форма; список отелей с бесконечным скроллом; карта, с отмеченными отелями, причем маркеры на карте подсвечиваются при наведении курсора на отель в списке; несколько фильтров, изменение состояния которых должно вызывать обновление как списка, так и карты — такой подход становится оправданным и весьма удобным.

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


Кастомизированная и обычная поисковая выдача для Москвы (хайрез).

Разметка и стили

Тщательная организация кода, разумеется, касается не только JavaScript, но и верстки: серверных и клиентских шаблонов и CSS. Требования к верстке те же самые, что и к JavaScript-приложению: модульность, гибкость, настраиваемость, безболезненное наследование. Нам повезло с тем, что существует достаточно хорошо устоявшаяся и зарекомендовавшая себя система организации кода, подходящая под эти требования. Это БЭМ (http://ru.bem.info/method/, http://clubs.ya.ru/bem/).

Для разметки и стилей используем БЭМ-методологию, правда с некоторыми особенностями. Во-первых, БЭМ не слишком хорошо подходит к нашей системе работы с JavaScript, поэтому js файлы храним отдельно и организуем в своем собственном порядке, а во-вторых для стилей используем SCSS (http://sass-lang.com/) вместо простого CSS.

Клиентские шаблоны

Из-за того, что большая часть логики реализована на JavaScript, мы активно пользуемся клиентскими шаблонами. Поначалу, мы использовали шаблонизатор, встроенный в библиотеку underscore.js (http://underscorejs.org/), но быстро поняли что это неудобно. Во-первых, хотелось хранить шаблоны в отдельных файлах, а во-вторых, хотелось, чтобы шаблоны были не строковыми переменными в JavaScript, в которых писать разметку в текстовых редакторах вроде vim подобно казни.

К тому же мы подумали о том, что неплохо было бы сделать эти шаблоны прекомпилируемыми, чтобы ресурсы машины пользователя не тратились на сложные преобразования строк.

К нам на помощь пришел наш отдел инфраструктуры, сделав для нас шаблонизатор. Мы назвали его JST — JavaScript Templates. Основной синтаксис оставили из underscore.js, но сделали так, чтобы шаблоны хранились во внешних файлах с расширением jst и автоматически компилировались в JavaScript-функции еще на этапе сборки проекта. Скомпилированные шаблоны подключаются к проекту как обычные js-файлы.

<!-- path/to/template.jst -->
<ul>
  <% elements.forEach(element, function(el, index) { %>
  <li><%= element.name %></li>
  <% }) %>
</ul>


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

// some_file.js
var template = templateNs['path/to/template.jst'];
var list = [
  { name: 'Первый' },
  { name: 'Второй' },
  { name: 'Третий' }
];

var renderedHtml = template.render({
  elements: list
});

document.body.innerHTML = renderedHtml;



Работа с клиентскими шаблонами стала невероятно удобной как для тех, кто занимается версткой, так и для тех, кто пишет JavaScript.

Зависимости и сборка проекта

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

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

Вернемся к примеру. Чтобы подключить в основном для страницы файле page.js, файлы base.js и searchform.js, нужно прописать зависимости прямо внутри page.js. Прописываются они в jsDoc, в теге @require. Получается, чтобы подключить нужные файлы, достаточно в файле page.js, в любом месте, например в блоке @fileoverview, добавить два тега:

/**
 * @fileoverview Page controller
 * @author Igor Alexeenko (twitter.com/iamo0)
 * @require ‘base.js’
 * @require ‘searchform.js’
 */



Все. Теперь в режиме отладки на страницу добавятся два тега <script>, которые подключат нужные файлы. А в продакшн режиме, медиагенератор склеит все эти три файла в один и пропустит через минификатор. Разумеется, он умеет следить за сложными зависимостями и никогда не подключит один файл несколько раз.

Помимо этого, именно медиагенератор компилирует jst-шаблоны и SCSS, кеширует картинки и делает всю черную работу по сборке проекта. Поэтому фронт-энд разработчикам остается только сосредоточиться на решаемой задаче.

К чему мы идем

Есть старая программистская шутка: любая достаточно сложная программа на Си или Фортране содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины языка Common Lisp.

Для фронт-энд разработчиков эту шутку можно перефразировать так: любой достаточно сложный проект, написанный на jQuery, Backbone или Knockout.js (http://jquery.com/, http://backbonejs.org/ или http://knockoutjs.com/), содержит заново написанную, глючную и медленную реализацию половины Google Closure Tools (https://developers.google.com/closure/).

Действительно, все о чем я здесь рассказал: модульность, ООП-подход, наличие базового набора модулей от которых можно наследоваться, работа со стилями, прекомпилируемые шаблоны — есть в экосистеме Google Closure. Помимо JavaScript-библиотеки Google Closure Library, в эту систему входит JS-минификатор Google Closure Compiler; шаблонизатор Soy, компилирующийся как в серверные, так и в клиентские шаблоны; и язык стилей, интегрированный с Google Closure Compiler и поэтому позволяющий минифицировать даже имена css-классов (голубая мечта любого разработчика, использующего БЭМ).

Closure Tools разрабатывается Google для их собственных проектов с 2005 года. Сначала библиотека писалась для нужд почтовика GMail, а потом с ее использованием были переписаны все остальные проекты Google. База кода одной только JavaScript-библиотеки Closure Library весит около 8Мб и содержит все, что нужно для разработки сложных проектов.

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

Все в этом мире уже написано каким-нибудь Пушкиным: большая часть задач, с которыми сталкиваются многие разработчики средних/больших проектов при разработке их с нуля, была уже решена инженерами Google в процессе написания их бесконечного количества сервисов.

Этой осенью готовится к выходу новая версия Островка, которая сейчас разрабатывается именно с использованием Google Closure. Основная особенность нового Островка в том, что это синглпейдж-приложение. Это значит, что вся логика по переходу между страницами, работе с адресной строкой, загрузкой данных и рендерингом страниц переместится на клиентскую часть. Сложность разработки проекта с лихвой компенсируется скоростью работы сайта и более удобным пользовательским интерфейсом.

Конечно, с переходом на Google Closure нам пришлось изменить кое-что в своем подходе к разработке, например, мы вынуждены были отказаться от БЭМ в силу некоторых недостатков этой методологии и ее слабой совместимости с нашей инфраструктурой для проекта на Google Closure, но взгляды на построение проекта у нас остались прежними.
Автор: @Ostrovok
Ostrovok.ru
рейтинг 39,36
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Комментарии (62)

  • 0
    Да, разбитие всего JS кода на кусочки — очень удобно при этом не используя предварительную сборку проекта (легче в местах возникновения ошибок ориентироваться). Правда я сначала застопорился с их подключением к системе, но теперь решил проблему.

    Полноценный фреймворк с роутером, контроллерами, моделями, вьюхам и маленькой, но полезной вещичкой как events, творят просто чудеса во время разработки.

    А перед публикацией, все сбивается в один файл, минифицируется и тада. Проект похудел на ~50% из-за минификатора и по причине наличия одного файла, начала загружаться значительно быстрее.
  • +3
    Говорят, не сразу ясно, что такое «автобусное число», если отвлечься от происхождения фразы, то автобусное число — это количество разработчиков, которых можно отпустить в отпуск одновременно
    • +1
      Не совсем. Автобусное число может быть равным числу разработчиков, а вот отпустить в отпуск одновременно всех разработчиков все-таки нельзя — кто-то же должен работать над проектом :)
    • +2
      Кстати, первый раз встречаю перевод «автобусное число», обычно всё-таки «автобусный фактор» как калька с оригинального bus factor.
      • 0
        Считайте это вольным переводом :)
      • 0
        Мы тоже используем «автобусное число».
    • +2
      Нет, не отпустить в отпуск. Именно задавить автобусом :) Подразумевается, что у этих людей не будет возможности «передать дела» и т.п.
  • 0
    Расскажите почему выбрали Closure, а не Dojo или Ext?
    • 0
      Если честно, мы не смотрели в сторону Dojo или Ext. Closure показался нам достаточно мощным, для решения всех наших задач, к тому же у нас в команде есть люди, у которых есть опыт разработки приложений именно на Closure. Ну и большим плюсом за Closure стала его мощная инфраструктура, в частности Closure Compiler, который лучше всего жмет код, написанный на Closure.
      • 0
        А не смотрели на angularjs? Шаблоны очень даже ничего, связанность низкая да вообще уже давно использую и все больше радуюсь )
        • +1
          Он нам не подошел. Шаблоны и низкая связанность — это не все условия, которые нам нужны, поэтому мы используем другое решение. Подробнее на вопрос почему именно Closure я отвечал ниже в комментариях.
  • +5
    Почему именно Google Closure?

    Если сравнивать со стандартными

    * require.js + r..js для сборки
    * Backbone + Chaplin (Marionette etc)
    * jQuery

    В чем получается преимущество? Набор готовых виджетов? Но я уверен, вы все равно пишите свои.
    Конечно, я думаю вы проводили сравнение, можете про него рассказать?
    • +1
      Основное преимущество Closure в том, что это цельный, мощный продукт.

      Помимо набора готовых виджетов там есть и встроенная система сборки зависимостей и шаблоны и минификатор кода и оптимизатор css-классов и все, что только можно придумать. Разумеется, мы пишем свои виджеты, но в качестве базового класса используем классы из Google Closure, такие как goog.events.EventTarget, goog.ui.Component, goog.ui.Control.

      В текущей версии островка, все написано на jQuery, но после того, как мы с его помощью реализовали мощный функционал, мы поняли, что повторяем то, что уже есть в Google Closure, просто на свой лад.

      Конечно, можно было взять набор из нескольких решений, jQuery как базовую библиотеку + Backbone, для MVC + еще что-нибудь для сборки зависимостей, но опыт показывает, что интегрировать разрозненные решения достаточно сложно. Наши коллеги, разрабатывающие Экстранет островка (http://habrahabr.ru/company/ostrovok/blog/155013/) сначала использовали Backbone, потом им пришлось частично от него отказаться и часть функционала реализовать на Knockout.js, потом выяснилось, что такие решения не рассчитаны на такие продукты как Экстранет и часть функционала им приходится реализовывать на чистом js. Мы, конечно же, учли этот опыт и постарались найти как можно более полную библиотеку, которая бы подошла для наших нужд.

      К тому же большим плюсом за Closure, стало то, что у нас есть в команде люди, которые имеют опыт работы с этой бибиотекой.
  • +1
    Перед тем, как выложить код, разработчик сам просит кого-то из товарищей проверить его код.


    Если не секрет, как это реализовано технически? Написавший код пишет ревьюверу в аську, тот подходит и из-за плеча читает код, давая вслух комментарии?
    • +2
      У нас есть специальный внутренний веб-ресурс для этого. Человек, который хочет выложить свой код, ставит галочки напротив имен тех, кто по его мнени должен посмотреть код и нажимает кнопку «Попросить ревью». Этим людям на почту приходят уведомления.

      Этот ресурс показывает все изменения, которые внес этот человек, либо в виде git diff'a, либо просто исходники изменившихся файлов. Ревьюеры могут давать свои комментарии по поводу кода через специальную форму. Если проще объяснить на словах, то тогда, можно и прокомментировать и из-за плеча :)

      Если ревьюер считает, что код можно публиковать, он нажимает на кнопку «Разрешить публикацию». Есть еще кнопка «Запретить». Код не будет выкачен, пока все ревьюеры не нажмут кнопку «Разрешить».
      • 0
        Серверная часть, которая показывает диффы, позволяет оставлять комментарии и так далее — самописная, или сделана на основе какого-нибудь готового решения?
        • 0
          Самописная.
        • +1
          Если вам интересны готовые решения позволяющие делать код ревью, то можно посмотреть на kiln (mercurial, можно сконвертировать из git).
    • +3
      У нас есть своя небольшая система code-review под названием WTF. Мы про нее еще отдельно расскажем, она очень клёвая.
      • 0
        Она самописная, или основана на каком-либо готовом решении?
        • +4
          Мы посмотрели на несколько решений code review: facebook phabricator, gerrit2 и конечно github pull requests. И написали свою очень простую (особенно по-началу) систему, которая заточена под git, feature branches и continuous integration. Расскажем подробнее и не исключено что заопенсорсим.
          • +1
            Если не секрет, чем Phabricator не понравился? Я его сейчас рассматриваю в качестве основного кандидата на внедрение.
            • +2
              * в фабрикаторе каждому разработчику придётся поставить клиент для работы с ним, который превносит дополнительные сложности к и так не простому процессу параллельной разработки в ветках
              * в нём нет никакой гибкости в настройках разрешений — кому можно отправлять код в master, а кому нет

              для маленькой команды, я бы скорее выбрал github
              • 0
                в фабрикаторе каждому разработчику придётся поставить клиент для работы с ним, который превносит дополнительные сложности к и так не простому процессу параллельной разработки в ветках


                Если не секрет, что за клиент? Насколько я понимаю, все действия по code review выполняются через веб интерфейс Phabricator, разработчикам ничего кроме веб браузера не нужно.
  • 0
    Не смотрели в сторону MVVM- фреймворков? AngularJS, EmberJS (может быть каких-то аналогов)? MVVM представляется более логичным и подходящим для фронт-енда по сравнению с MVC. Опять-же, верстальшики будут в восторге, да еще и смогут помимо верстки добавлять базовую динамику для вьюх.
    • +1
      Мы изучили много вариантов перед тем, как остановиться на Closure. Самые распространенные MVVM и MVC библиотеки показались нам недостаточно хорошо подходящими для нашего проекта. Полнее я отвечал в комментариях выше.
  • +1
    Почему вы взяли за основу синтаксиса шаблонов underscore.js? Он же исполнен синтаксическим шумом.
    Как вы устанавливаете связь между css/scss и шаблонами? По структуре каталогов?
    Устанавливается ли связь между конкретной «вьюхой» на клиентской стороне и шаблоном?
    Как вы планируете сжимать имена классов в будущем, если у вас они вшиты в шаблоны?
    Какие идентификаторы элементов используете для связи js с DOM?
    Также я успел заметить что в css используются имена тэгов и вложенность, это несколько несоответствует методологии BEM. Часто ли приходится решать конфликты «веса» селекторов при таком подходе?

    Заранее спасибо.
    • +2
      underscore.js мы взяли за основу потому что это решение напрашивалось само. Мы использовали библиотеку underscore в своем проекте, поэтому не пришлось далеко ходить за решением. К тому же у нас на глазах уже был опыт Экстранета, который в то время писал на Backbone, а в нем, как вы знаете используется underscore.

      По поводу синтаксического шума, позволю себе с вами не согласиться — шаблоны underscore достаточно лаконичны и в то же время читаемы. Мы одно время смотрели на Mustache, но они показались нам слишком уж простыми. Кое-какую js-логику иногда полезно вынести в шаблон. Разумеется в пределах разумного.
      • 0
        Конечно я не считаю шаблоны mustache/handlebars достаточно хорошими, и прежде всего я бы предложил избавиться от xml нотации DOM элементов. Например bem-tools использует синтаксис на основе json'а, некоторые шаблонизаторы indent-based (a la haml/jade/etc.), некоторые исользуют сам язык программирования как dsl для шаблонов и так далее.
        • 0
          Поскольку, у нас с шаблонами работают в том числе и верстальщики, для нас важно, чтобы код шаблона был прост и привычен. Именно поэтому мы решили не отказываться от XML-нотации DOM-элементов. Поэтому шаблонизатор, в котором используется HTML + JS, показался нам достаточно удобным.
    • +1
      Связь между серверными шаблонами и scss на текущем сайте устанавливаем по структуре каталогов, да. Клиентские шаблоны зачастую тоже удобно положить рядом с ними, но не всегда. Мы не связываем напрямую клиентский шаблон и «вьюху» (кстати, мы не используем этот термин), мы предпочитаем развязывать себе руки и действовать более гибко. К тому же есть шаблоны, которые могут использоваться многократно в разных «вьюхах».

      Для связи js и DOM используем обычные имена css-классов, никаких специальных классов, только для js у нас нет. Для сжатия классов в новой версии мы используем встроенные в Google Closure инструменты, которые никаким образом не зависят от используемого языка шаблонов, главное, чтобы он компилировался в js.

      В текущей версии сайта мы используем «чистый» БЭМ и названия наших классов полностью соответствуют БЭМ-методологии, в примере я его не использовал для чистоты примера, ровно чтобы показать как мы пишем js-код, не отвлекаясь на детали.
      • +2
        Прелесть BEM'а не только в универсальной конвенции, а ещё и в возможности сгенерировать элементы блока втоматически, задавая им только имена элементов.

        В случае ручной работы с методологией BEM она слабо раскрывает свои достоинства.
        • +1
          Да, вы правы. Вообще в перспективе выяснилось, что БЭМ не слишком хорошо подходит для нашего проекта (а может мы просто не умеем его готовить) и в новой версии островка мы решили от него отказаться в пользу более простой системы.
          • +2
            У команды БЭМ есть практика выездных приватных семинаров. Разработчики составляют свои вопросы, а мы, подготовившись, приходим на чай с плюшками. Так общение получается более продуктивным, вопросы рассматриваем с учётом архитектуры ваших проектов. Можем к вам придти :-)
            Я не к тому, чтобы вы всё взяли и переделали, а просто если есть предположение, что «не умеем готовить» и просто любопытно, как это сделали бы мы, то мы всегда готовы поделиться.
            • +1
              Здорово, мы будем рады пригласить вас в гости и послушать про БЭМ из первых рук.
  • +2
    Посмотрел главную страницу вашего сайта через Firebug быстро, немного удивили 60 с чем-то загружаемых изображений. Особенно с учетом того, что бОльшую их часть я на странице, потыкавшись несколько минут в интерфейсе, так и не нашел.
    Конечно, скорее всего я просто чего-то не понял, но там точно ничего лишнего не грузится? :)

    И логотипы в «пресса об Островке», наверное, меняются довольно нечасто. Может лучше их в спрайт склеить, нет?

    Ну это так, в порядке мыслей вечером в понедельник.
    • +1
      Да, Михаил, мы знаем про этот недостаток. Это картинки для блока «Популярные направления» и их действительно лучше склеить в спрайт и не загружать до тех пор пока пользователь не откроет этот блог. Мы работаем над этим и, в скором времени, исправим.

      Спасибо за фидбек.
  • +2
    Еще такой вопрос: пробовали ли использовать CoffeeScript? Может быть вполне актуально при таком количестве JS-кода.
    • 0
      Мы думали о CoffeScript, один из наших разработчиков, который имел большой работы работы с ним, даже провел нам лекцию, но мы не нашли никаких больших плюсов, чтобы перейти на него. Все удобные обвязки для классов, итераторов и пр., у нас есть и так (в текущей версии в виде наших решений и библиотеки underscore, а в будущей — в Google Closure), а вот синтаксис достаточно непривычный.

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

      Поэтому не используем.
      • +1
        Ну на самом деле используем, просто не в островке, а в некоторых наших других продуктах. Возможно и в островке будет, но пока да, на основном сайте ничего нет.
      • +1
        С дебагом и правда есть некоторые накладки. Но обычно проявляются, если кто-то не вполне понимает, как именно CoffeeScript транслируется в JavaScript.

        А вот это вполне себе аргумент: «Все удобные обвязки для классов, итераторов и пр., у нас есть и так (в текущей версии в виде наших решений и библиотеки underscore, а в будущей — в Google Closure), а вот синтаксис достаточно непривычный.»

        Но могу добавить, что синтаксис — дело привычки. К нему так привыкаешь (к лучшим сторонам), что потом очень не хочется «обратно».
  • +2
    К слову — а на чем сделан бэк-энд (веб-фейс, app-server, итд)?

    И вообще — будет про него статья? Островок — крутой и навороченный проект, очень интересно, как устроен внутри!
    • 0
      Бекенд на питоне. Статьи, надеюсь будут, следите за нашим блогом.
    • +1
      Ага, на бэкэнде python, django, postgres, redis и celery как основной стек технологий. Есть еще всякая экзотика по-мелочи, типа эрланга )). Про бэкэнд тоже на хабре напишем еще.
      • 0
        Было бы здорово! Как масштабируется, где какие данные хранятся — самое интересное :) Ну и процесс, как все это разрабатывается, поддерживается.
        • 0
          ок, расскажем. про процессы расскажем тоже. и про деплой.
  • –1
    Хочу спросить никто не решал такую проблему с шаблонизатором: проеткт на backbone+require.js шаблоны на underscore подгружаются плагином !text. Без оптимизации все работает нормально — шаблоны подгружаются из файлов. Если собирать проект как описанно в документашке к require — то шаблоны включаются в общий файл. что совершенно не нужно. Требуется чтоб шаблоны были отдельно и в любой момент их можно было отредатировать.
  • 0
    9 человек на фронтенде — это имеется в виду только кодеры? Компетенции как-то разделены, или все могут всё?
    • 0
      Да, именно кодеры. Компенетции разделены — как по специализации человека, так и по сфере деятельности — у каждого из разработчиков своя область, за которую он отвечает. Специалист по странице отеля, по картам, по поисковой выдаче, по лендингам и так далее.
  • +1
    Что-то не совсем понятно. Вы нахваливаете стек от Google, при этом используете свой шаблонизатор JST, и свою систему разрешения зависимостей и сборщик (что вроде как делается через goog.require/goog.provide с последующей сборкой closure compiler). И еще Вы пишите:
    и язык стилей, интегрированный с Google Closure Compiler и поэтому позволяющий минифицировать даже имена css-классов (голубая мечта любого разработчика, использующего БЭМ)

    Судя по всему вы это тоже не используете, но можно поподробней?
    И правильно ли я понял, что вы используете только утилиты из closure library, а модель компонентов UI у вас своя?
    • 0
      Все просто. В статье речь идет о двух версиях сайта Островок — текущей и той, которая в скором времени выйдет. Свои решения мы используем сейчас, а в новой версии заменим их на решения, которые входят в стек Google Closure.

      С моделью компонентов та же история — сначала мы написали свою, но в грядущей версии мы активно используем встроенные в Closure компоненты.
      • 0
        Выходит все примеры для старой версии? Тогда было бы интересней увидеть что было и как стало, примеры шаблонов и т.д.
        Если не секрет, сколько времени по планам будет затрачено на переход на новую версию?
        • 0
          Все примеры для текущей версии. Обзор как было и как стало — это уже материал для отдельной статьи.
  • 0
    Коллеги, а как подружили питон и Closure Tools? Или вы не используете Closure на стороне сервера совсем?
    • 0
      Не используем :) Единственное что нам нужно со стороны сервера от Closure, так это plovr, но он не пересекается с основными задачами бекендеров вообще.
  • 0
    Можно я немного яда подолью? :-) Про фронтэнд?

    Как сейчас дела обстоят я не знаю — это было в июне-июле этого года.

    У Вас в экстранете для включение / выключения чего-либо используется грид из кучи таких переключателей-движков.
    Мало того что это жутко неудобно — вообще экстранет должен быть максимально простым — а тут глаза от ряби дизайна выпадывают, так еще и жутко все это счастие тормозило.

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

    Сидел я на хреновом канале и не на сильном компе — надо было срочно убрать номера из бронирования. Так вот при переключении КАЖДОГО переключателя в гриде гдето к 5-7 штукам начинало все жутко при жутко тормозить.

    Я глянул код — там класс на классе и классом погоняет вобщем при щелчке там что-то порождается и затем отсылается на сервак, по скольку канал нестабильный и узкий — эти кучки плодились и плодились и потом минуту надо было курить и ждать пока все уйдет.

    Ведь можно баланс какойто делать, не все оопешить? это выдливается в тонны ненужных классов и тормоза при работе ибо бинд на чтото вызывает и порождает 15 классов, а не вызывает одну единственную функцию.
    Зачем так усложнять? Зачем рисовать дизайнерские фенечки вместо простого и понятного грида? Выглядит менее красиво? Да, но это экстранет а не витрина в магазине.

    Нет я пониимаю что ООП дает модульность, дизайн — красоту и все такое — но что из этого получается — я описал.

    Желаю Вам устранить все эти недостатки и развиваться дальше — проект нужный и замечательный, я посмотрю чем оно все закончится и быть может снова начну пользоваться.
    • 0
      Мне кажется, что вы немного путаете понятия. Дело в том, что сам факт использования или не использования ООП никоим образом не влияет на качество конечного продукта. Можно написать плохой код и в функциональном стиле: дело не в этом. Это примерно так же как не важно как собирается на заводе ваш телефон: вручную или умными роботами — главное что он хорошо работает. Стоит разделять понятие инструмента и конечного продукта.

      Конечно бывают случаи, когда инструмент неподходящий, можно пытаться ножом колоть дрова, но в случае с разработкой интерфейсов и ООП этот пример не срабатывает, потому что этот подход давно себя зарекомендовал среди разработчиков сайтов. Посмотрите на продукты Google и поймите что дело не в том какой подход использовать.

      Перед нашим экстранетом стоят нетривиальные архитектурные задачи и они постоянно работают над улучшением своего продукта. Думаю, вам как человеку пользовавшемуся нашим экстранетом и разбирающемуся в программировании будет интересно посмотреть их доклад с 404-феста (http://habrahabr.ru/company/ostrovok/blog/155013/), где они рассказывают про свое приложение, про проблемы с которыми они сталкиваются и как они их решают.

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

      Спасибо вам за фидбек и пожелания. Мы постоянно развиваемся и стараемся стать лучше.
    • 0
      Мы знаем про кучу проблем в экстранете, особенно с производительностью. В ближайшие несколько недель выйдет большой апдейт фронтэнда, который решит многие проблемы. Большое спасибо за фидбэк, мы постараемся учесть и все остальные пожелания в будущих версиях.
  • 0
    Возникло несколько вопросов:
    1. Используется/планируется автоматическая сборка изображений в спрайты?
    2. Для код-ревью смотрели ли в сторону Crucible?
    3. В чем именно заключался отказ от БЭМ при переходе на Google Clousure, т.к. сейчас именование классов идет в духе БЭМ, либо имеется ввиду организация файловой системы проекта?

    Спасибо за шаринг опытом. Было бы здорово, если бы поделились опытом, как прошел собственно переход на Google Closure.
    • +1
      1. Используется.

      2. Нет, не смотрели, потому что нас полностью устраивает наша система)

      3. БЭМ используется в той части сайта, которая еще не была переписана на Closure, если вы обратите внимание на главную страницу сайта и посковую выдачу, то там БЭМ не используется.

      Вообще, по поводу новой версии сайта готовится не менее подробный пост. Stay tuned)
      • 0
        Спасибо за ответы! По поводу автоматической сборки изображений: а какое именно решение вы применяете?

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

Самое читаемое Разработка