История
Итак, у вас и у вашего партнера появилась замечательная бизнес-идея. Верно? Вы постоянно добавляете в уме все новые и новые возможности. Вы регулярно спрашиваете у потенциальных клиентов их мнение, и все они без ума от вашей идеи. Окей, значит людям это нужно. На этом можно даже заработать денег. И единственная причина, по которой люди до сих пор этим не пользуются: вы не реализовали свою идею. Пока не реализовали.
И наконец, в один прекрасный день вы решили: «Сделаем это!». И вот вы уже пытаетесь разобраться как реализовать бизнес-логику своего приложения, ту киллер-фичу, которая будет двигать продукт вперед. У вас есть идея как это сделать, и вы знаете, что способны на это.
И вот вы говорите: «Готово! Работает!» У вас есть успешный прототип! Осталось только упаковать его в веб приложение.
«Окей, сделаем сайт,» – говорите вы.
А только потом вы понимаете, что для этого нужно выбрать язык программирования; нужно выбрать (современную) платформу; нужно выбрать какие-то (современные) фреймворки; нужно настроить (и купить) хранилище, базы данных и хостинг; нужно обеспечить интерфейс для администрирования; нужно обеспечить контроль доступа и систему управления контентом. Вы хотите быть бережливым (lean) и гибким (agile). Вы хотите использовать технологии, которые помогут вам быть успешным как в краткосрочной, так и в долгосрочной перспективе. А выбрать их далеко не всегда так просто.
Перед вами десятки и десятки архитектурных решений, которые необходимо принять. И вы не хотите ошибиться: требуются технологии, которые позволят вести быструю разработку, поддерживают постоянные итерации, максимальную эффективность, скорость, устойчивость и многое другое. Вы хотите быть бережливым (lean) и гибким (agile).
Вы хотите использовать технологии, которые помогут вам быть успешным как в краткосрочной, так и в долгосрочной перспективе. А выбрать их далеко не всегда так просто.
«Я перегружен», – говорите вы и чувствуете себя перегруженным. Энергия уже не та, что была в начале. Вы пытаетесь собраться с мыслями, но работы слишком много.
Прототип медленно блекнет и умирает.
Предложение
После того, как я забросил кучу идей по похожим причинам, я решил спроектировать решение для этой проблемы. Я назвал этот проект ‘Init’ (или init.js).
Основная идея – использовать один проект для старта любого проекта, дать возможность разработчику или техническому руководителю принять все основные решения за раз и получить подходящий начальный шаблон, основанный на них. Я знаю, многие скажут «Нельзя применить одно решение ко всем проблемам» (haters gonna hate). И они, возможно, правы. Но мы можем постараться создать подходящее в целом решение, и, на мой взгляд, Init с этой задачей справился.
Чтобы достичь этой цели, необходимо принять во внимание несколько важных моментов. При разработке Init я сделал упор на следующем:
-
Компоненты
Компонентное представление – одна из ключевых характеристик любой системы, так как она позволяет повторно использовать компоненты программного обеспечения в нескольких проектах, что является основной целью Init. Но компонентное представление содержит в себе побочный эффект – заменяемость, которая станет нашим основным союзником в борьбе с различными проблемами, решение которых «почти» одинаково.
-
Простота разработки
Какая-то проблема где-нибудь имеет решение, лучше всего реализованное на Brainf*ck. будет практически невозможной для написания, не говоря уже о чтении. Это будет стоить вам времени и громадных усилий. В целом, вы должны использовать языки и платформы, которые упрощают, а не усложняют разработку для вас (и тех, кто будет делать ее позже).
-
Сообщество
Какую бы платформу вы не выбрали, убедитесь, что вокруг нее существует большое сообщество. Такое, которое может помочь вам с большинством стандартных и нестандартных проблем. Помните: jQuery, возможно, не самая быстрая, чистая, и элегантная библиотека, но она – победитель, благодаря своему сообществу.
Я покажу как принимал решения при создании Init, не забывая про эти цели.
В сердце Init – парадигма full-stack JavaScript – «полный набор JavaScript» (некоторые люди называют ее или ее часть MEAN Stack). Работая с таким набором, Init позволяет использовать лишь один язык для создания удивительно гибких и полнофункциональных окружений для разработки веб приложений. Короче говоря, Init позволяет использовать JavaScript не только для разработки клиентских и серверных решений, но и для сборки, тестирования, шаблонизации и так далее.
Но давайте остановимся на мгновение и спросим себя: такая ли хорошая этo идея – использовать JavaScript?
Почему я выбрал JavaScript
Я работаю веб-разработчиком с 1998 года. В то время мы по большей части использовали Perl для серверной разработки, но даже тогда у нас был JavaScript для клиентской части. Технологии веб-серверов очень сильно изменились с тех пор: мы прошли через несколько волн языков и технологий, таких как PHP, AP, JSP, .NET, Ruby, Python. Разработчики начали осознавать, что использование двух разных языков в серверной и клиентской части усложняет работу. Изначально попытки объединить две части под одним языком вылились в создание клиентских компонентов на сервере и компиляцию их в JavaScript. Результаты не оправдали ожиданий, и большая часть таких проектов провалилась (например, ASP MVC, заменивший веб-формы ASP.NET, и GWT который, судя по всему, в ближайшем будущем будет заменен Polymer‘ом). Но, по сути, это была замечательная идея: один язык для клиента и сервера, позволяющий повторно использовать компоненты и ресурсы (это ключевое слово: ресурсы).
Ответ был простым: перенести JavaScript на сервер.
На самом деле JavaScript родился с серверной стороной в Netscape Enterprise Server, но язык просто-напросто не был готов на тот момент. Спустя годы попыток и неудач, появился, Node.js который не только перенес JavaScript на сервер, но также продвинул идею неблокирующего программирования, навсегда изменив то, как мы пишем “fread” (I/O) (больше об этом читайте здесь).
В двух словах: неблокирующее программирование нацелено на перенос продолжительных по времени задач в сторону. Обычно это делается определением того, что должно произойти когда задача завершится, что позволяет процессору заниматься другими запросами в это время.
Но эти идеи не были новыми, почему же они стали так популярны с приходом Node.js? Простое неблокирующее программирование достигается несколькими способами. Пожалуй, самый простой это использовать обратные вызовы (callbacks) и цикл событий – event loop. В большинстве языков это непростая задача: если обратные вызовы это довольно распространенная функция, то цикл событий – нет, и в какой-то момент вы оказываетесь в схватке с внешними библиотеками (например: Python, с Tornado). Но в JavaScript обратные вызовы это часть языка, как и цикл событий, и каждый программист, который хотя бы пробовал JavaScript, знаком с ними (или как минимум использовал их, даже если не до конца понимал что такое event loop).
Внезапно, любой стартап на Земле может повторно использовать разработчиков (читай: ресурсы) и на клиентской и на серверной стороне, решая кадровую проблему. «Нам нужен гуру Питона».
Итак, теперь у нас есть невероятно быстрая платформа (спасибо неблокирующему программированию) с языком, который очень легко использовать (спасибо JavaScript). Но достаточно ли этого? Будет ли это работать? Я уверен что JavaScript займет важное место в будущем. Позвольте объяснить почему:
-
Функциональное программирование
JavaScript был первым языком программирования, принесшим парадигму функционального программирования в массы (конечно, первым был Lisp, но большинство программистов ни разу не создавали полностью завершенный продукт на Lisp). Lisp и Self, основные языки, повлиявшие на JavaScript, полны инновационных идей. Эти идеи могут освободить наш разум для изучения новых техник, шаблонов проектирования и парадигм. Все они перешли в JavaScript. Взгляните на монады, числа Черча, или даже (более практический пример) функции коллекций Underscore.js которые могут уберечь вас от множества строк лишнего кода.
-
Динамическое программирование и прототипное наследование
Объектно-ориентированное программирование без классов (и без бесконечной иерархии классов) позволяет ускорить разработку (создай объекты, добавь методы и используй их) но, что важнее, уменьшает время рефакторинга при последующей поддержке, позволяя программисту изменять готовые объекты вместо того, чтобы изменять классы. Эта скорость и гибкость создает условия для быстрой разработки.
-
JavaScript это интернет
JavaScript был спроектирован для интернета, он здесь с самого начала, и он не собирается никуда уходит. Все попытки уничтожить его провалились: посмотрите, к примеру, на падение Java апплетов, замену VBScript’а TypeScript’ом от Майкрософт (который компилируется в JavaScript), смерть Flash от рук мобильного рынка и HTML5. Невозможно заменить JavaScript не разрушая миллионы веб страниц, так что нашей целью должно быть улучшение языка. И никто для этой задачи не подходит лучше, чем Technical Committee 39 из ECMA.
Да, альтернативы JavaScript’у рождаются каждый день, например, CoffeeScript, TypeScript и миллионы языков, которые компилируются в JavaScript. Эти альтернативы могут быть полезными на этапах разработки (благодаря source maps), но им не удастся заменить JavaScript в долгосрочной перспективе по двум причинам: их сообщества никогда не станут больше, и их лучшие возможности будут реализованы в ECMA Script (читай: JavaScript). JavaScript это не язык ассемблера, это высокоуровневый язык программирования с исходных кодом, который вы можете понять, так что вы должны понять его.
Благодаря проекту Esprima project вы можете создавать собственные инструменты для работы с исходным кодом: код можно модифицировать, менять стиль, добавлять компоненты, инструменты и любые другие вещи, которые можно себе представить играя с Абстрактным Синтаксическим Деревом вашей программы как если бы это было дерево DOM.
JavaScript от начала до конца: Node.js и MongoDB
Итак, это были причины выбрать JavaScript. Теперь я попробую убедить вас использовать Node.js и MongoDB.
-
Node.js
Node.js это платформа для создания быстрых, масштабируемых сетевых приложений – примерно так его описывает официальный сайт. Но Node.js это больше, чем просто платформа. Это предпочтительное окружение для запуска JavaScript-приложений с доступом к устройствам ввода/вывода. Даже если вы не планируете писать основное серверное приложение на Node.js, вы можете использовать инструменты, созданные на основе Node.js, чтобы улучшить процесс разработки. Например, Mocha.js для юнит тестов, Grunt.js для автоматической сборки, или даже Brackets для полнотекстового редактирования кода.
Так что если вы планируете писать приложения для сервера или клиента на JavaScript, вы должны познакомиться с Node.js, потому что он будет вам необходим каждый день. Существуют некоторые интересные альтернативы, но ни у одной из них нет и 10% сообщества Node.js.
-
MongoDB
MongoDB это документо-ориентированная NoSQL база данных, которая использует JavaScript в качестве языка запросов, позволяя замкнуть цикл работы с платформой JavaScript. Но это не главная причина использовать MongoDB.
MongoDB не требует описания схем таблиц благодаря чему вы можете с легкостью сохранять объекты, и таким образом быстрее приспосабливаться к изменениям в требованиях. К тому же, MongoDB хорошо масштабируется и использует map-reduce, что делает ее хорошим выбором для приложений с большими данными. MongoDB настолько гибка, что может использоваться в качестве документной базы данных без схемы, реляционного хранилища (но без транзакций), или даже хранилища ключ-значение для кеширования.
Серверное компонентное представление с Express.js
Серверное компонентное представление – сложная задача. Но с Express.js (и Connect.js) появилась идея «промежуточного слоя» (middleware). По моему мнению, промежуточный слой это лучшее определение компонентов сервера. Если хотите сравнить его с известным шаблоном проектирования, то это что-то наподобие конвейеров и фильтров.
Основная идея в том, что ваш компонент – это часть конвейера. Конвейер обрабатывает запрос (input) и генерирует ответ (output), но ваш компонент не ответственен за весь ответ. Напротив, он лишь модифицирует то, что необходимо, а затем передает задачу следующему элементу конвейера. Когда последний элемент конвейера заканчивает обработку, ответ посылается обратно клиенту.
Мы называем эти «элементы конвейера» «промежуточным слоем». Очевидно, что можно создать два типа промежуточных слоев:
-
Посредники: элементы, которые обрабатывают запрос и ответ, но на них нет полной ответственности за сам запрос, так что они делегируют запрос следующему слою.
-
Конечные элементы: элементы, полностью ответственные за конечный ответ. Они обрабатывают и модифицируют запрос и ответ, но не должны делегировать их следующим слоям. На практике, возможность делегирования следующему слою все равно предпочтительна для архитектурной гибкости (например, для добавления еще одного слоя), даже если этот слой не существует (в случае чего ответ отправляется напрямую клиенту).
В качестве примера, представьте себе серверный компонент для управления пользователями. В плане промежуточных слоев мы имеем как конечные элементы, так и посредников. Конечные элементы должны иметь возможность создавать пользователя и выводить список пользователей. Но вначале нам нужны посредники для аутентификации (так как мы не хотим, чтобы не аутентифицированные запросы могли создавать пользователей). После того, как мы создали посредника для аутентификации, мы можем просто встроить его в любое место, где хотим превратить ранее открытую возможность в аутентифицированную.
Одностраничные приложения
Проект Init фокусируется на создании одностраничных приложений - ‘single page applications’ (SPAs). У большинства веб-разработчиков не раз был соблазн попробовать себя в создании одностраничных приложений. Я сделал несколько (в основном для себя), и могу с уверенностью сказать, что за ними будущее веб-приложений. Вы когда-нибудь сравнивали SPA с обычным веб-приложением на мобильном соединении? Разница в отклике составляет десятки секунд.
Одностраничные приложения – это будущее интернета, так что зачем создавать свой продукт в устаревшем формате? Частый аргумент, который я слышу – это трудности с SEO. Но если вы реализуете все правильно, это не будет проблемой: сам Google предлагает очень хорошую инструкцию как это сделать, да и здесь есть неплохие комментарии.
Клиентский MV* с Backbone.js, Marionette.js, и Twitter Bootstrap
Многое было сказано про MVC* фреймворки для одностраничных приложений. Это сложный выбор, но я бы сказал, что три фаворита – это Backbone.js, Ember.js, и Angular.js.
Все они считаются очень хорошими. Но какой будет лучшим для вас?
К сожалению, вынужден признаться, что у меня очень мало опыта с Angular.js, так что я исключу его из этой дискуссии. Итак, Ember.js и Backbone.js представляют собой два разных пути для решения одной проблемы.
Backbone.js минимален, прост и предлагает вам необходимый минимальный набор для написания простого SPA. Ember.js же - это полноценный и профессиональный фреймворк для создания одностраничных приложений. В нем больше возможностей, но и кривая обучаемости круче.
В зависимости от размера приложения, решение может быть таким же простым, как анализ отношения «используемые функции / доступные функции». Оно даст вам хорошую подсказку.
В случае с Init, я хотел покрыть большинство сценариев, поэтому выбрал Backbone.js для простого создания SPA, с Backbone.Marionette.View для компонентизации. В такой схеме каждый компонент - это простое приложение, а конечный продукт может быть настолько комплексным насколько я захочу.
Стилизация – это тоже большая задача, но мы в очередной раз можем рассчитывать на фреймворки. Для CSS нет ничего лучше Twitter Bootstrap, в нем есть полный набор стилей, которые прямо “из коробки» готовы не только к использованию, но и к удобной модификации.
Bootstrap был создан с использованием языка LESS Bootstrap – проект с открытым исходным кодом, так что его можно модифицировать если нужно. В комплекте с ним куча элементов пользовательского интерфейса с хорошей документацией на сайте Bootstrap. PК тому же присутствует модель для модификаций позволяющая без труда создать собственные элементы. Несомненно, это лучший выбор для нашей задачи.
Лучшие методики: Grunt.js, Mocha.js, Chai.js, RequireJS, и CoverJS
Наконец, мы можем выделить лучшие методики и выяснить, как Init может помочь нам в их реализации и последующей поддержке. Наше решение основано на нескольких инструментах, каждый из которых в свою очередь основан на Node.js.
-
Mocha.js и Chai.js:
Эти инструменты дают возможность управлять процессом разработки с применением TDD или BDD, предоставляют инфраструктуру для организации юнит тестов и позволяют автоматически запускать их.
Существуют тысячи фреймворков для юнит тестов на JavaScript. Почему стоит использовать Mocha.js? Короткий ответ: он гибкий и законченный.
Длинный ответ: у него есть две важные особенности (интерфейсы, репортеры), и в нем нет библиотеки утверждений (assertions) Позвольте объяснить.
Chai.js это гибкая библиотека утверждений, которая позволяет использовать любой из трех основных стилей:
Chai.js отлично сочетается с Mocha.js. Используя эти две библиотеки можно писать юнит тесты в TDD, BDD или любом другом стиле, который можно себе представить.
-
Интерфейсы: возможно, вы привыкли к концепциям TDD вроде юнит тестов и коллекций сценариев (suite), возможно, вы предпочитаете идеи BDD со спецификациями поведения «описаниями» и «это должно». Mocha.js поддерживает оба подхода.
-
Репортеры: запуск юнит тестов генерирует отчеты о результатах, и вы можете форматировать эти отчеты с помощью разных репортеров. Например, если вам нужно подавать информацию серверу непрерывной интеграции, то можно подобрать репортер специально для этой задачи.
-
Отсутствие библиотеки утверждений (assertion): Mocha.js был создан так, что с ним можно использовать любую библиотеку утверждений, что улучшает гибкость. Существует много вариантов, но тут стоит рассмотреть Chai.js.
-
Утверждение (assert): классический стиль утверждений из старой школы TDD, например:
assert.equal(variable, "value");
-
Ожидание (expect): Стиль цепных утверждений, чаще всего используется в BDD. Например:
expect(variable).to.equal("value");
-
Должен (should): Также используется в BDD, но я предпочитаю «ожидание» потому что «должен» звучит созвучно с спецификацией поведения ‘он («должен делать что-то»)’. Например:
variable.should.equal(“value”);
-
-
Grunt.js:
GGrunt.js позволяет автоматизировать сборку, начиная с простого копирования-вставки и склеивания файлов, до прекомпиляции шаблонов, компиляции метаязыков для стилей (т.е., SASS и LESS), юнит тестирования (с mocha.js), анализа и минификация кода (например, с UglifyJS или Closure Compiler). Вы можете добавить собственные автоматизированные задачи в Grunt, или найти нужное решение среди сотен и сотен существующих плагинов, (опять же, использование инструментов, за которыми стоит отличное сообщество, играет нам на руку). Grunt также может следить за файлами и запускать действия при их изменении.
-
RequireJS:
RequireJS может показаться просто очередным способом загрузки модулей наряду с AMD, но я уверяю вас что RequireJS способен на большее. Чтобы понять почему, во-первых, нужно упомянуть идею области видимости модуля (например, demo.views.hello), которая помогает держать глобальную область видимости чистой, скрывая каждый модуль в собственной области. Проблема в том, что эти модули нельзя использовать повторно: если вы измените namespace одного из экземпляров, это затронет все экземпляры. RequireJS, в свою очередь, дает возможность изначально создавать модули для повторного использования. (К тому же он помогает использовать внедрение зависимости (Dependency Injection), чтобы ваши модули не обращались к глобальным переменным.
-
CoverJS:
Покрытие кода – это мера оценки тестирования. Из названия понятно, что библиотека дает информацию о покрытии кода вашей текущей коллекцией тестов. CoverJS оценивает покрытие кода тестами инструментацией инструкций (а не через строки кода, как JSCoverage) и генерацией инструментальной версии вашего кода. Он также может генерировать отчеты для сервера непрерывной интеграции Server.
Использование веток для переключения возможностей
Я начал работу над Init и мне нужен был способ для включения и выключения различных возможностей, которые могут понадобиться в проекте. Я выбрал радикальный подход: использование веток git’а для реализации этой задачи.
Грубо говоря, каждая ветка представляет собой компонент или функциональность, которую пользователь может пожелать включить. Если вы создаете проект с нуля, начните с минимальной ветки, а потом добавляйте другие технологии слиянием с нужными ветками. Скажем, например, что нам нужно начать проект с Backbone.js и Marionette.js. Можно начать с ветки Backbone.js и слить ее с веткой Marionette.js, добавляя в будущем каждую необходимую вам возможность.
Пока такая идея слияния для добавления функциональности может быть использована для технологий-шаблонов (вроде Backbone, Node, Express). Но в будущем вы сможете переключаться между бэкэндами (например, с MongoDB на Postgres) и клиентскими решениями.
Начните проект с Init и разверните на Heroku сегодня
Еще никогда не было более простого способа начать проект. Просто перейдите в репозиторий GitHub, выберите ветку с последними коммитами (сейчас – usermanager, однако это может изменится в будущем) и потом:
-
Создайте директорию для своего проекта (или используйте существующую).
-
Создайте репозиторий с помощью git init (или используйте существующий репозиторий).
-
Добавьте удаленный репозиторий init
git remote add init git://github.com/picanteverde/init.git
-
Получите нужную ветку
git pull init usermanager
-
Получите процесс-файл для heroku
git pull init heroku-webprocess
-
С установленным Heroku Toolbelt создайте приложение Heroku
heroku create
-
Отправьте вашу мастер ветку в Heroku
git push heroku master
-
Посетите ваше уже работающее приложение в Heroku!
Теперь вы можете начать разрабатывать свою киллер-фичу с помощью нескольких строк кода. При этом вы будете вести разработку с помощью самых свежих и эффективных технологий, автоматизированных насколько это возможно.
Надеюсь, вы сможете использовать Init для быстрой реализации вашей новой большой идеи. Не забывайте проверять репозиторий Init на появление новых функций и исправлений багов, так как проект находится в активно развивается. Ну а я с нетерпением жду ваших отзывов.
Оригинал статьи: Init.js: A Guide to the Why and How of Full-Stack JavaScript. Aвтор: Alejandro Hernandez (JavaScript разработчик в Toptal). Перевод Рахим Давлеткалиев
Фото: flickr.com
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.