Предлагаю читателям «Хабрахабра» перевод понравившейся мне статьи «After a year of using NodeJS in production» за авторством Gavin Vickery. Это продолжение его статьи «Why I’m switching from Python to Node.js», которую он написал чуть больше года назад в ответ на разочарования при использовании Python и как обоснование перехода на Node.
Год разработки с штатными инструментами командной строки, клиентские проекты и выпуск обновлений для продуктов нашей компании, в двух словах, это то, чему я научился за это время. Я бы хотел поделиться опытом, но речь пойдет не столько про Node, сколько за весь JavaScript в целом.
Node очень прост для освоения. Особенное если вы знакомы с JavaScript. Нагуглите несколько туториалов для начинающих, поиграйтесь с Express и вы уже в теме, не так ли? Затем вы начнете осознавать, что нужно как-то взаимодействовать с базами данных. Нет проблем, запускаете NPM… Ага, всего лишь горстка приличных SQL пакетов. Позднее вы поймете, что все существующие ORM инструменты никуда не годятся и то, что базовый драйвер — это лучший выбор. Теперь у вас проблемы c реализацией своей модели и проверкой логики. Скоро после этого, вы начнете писать более сложные запросы и просто потеряетесь в callback'ах. Естественно, вы прочитаете про 'callback hell', плюнете на это дело и начнете использовать одну из многих promise библиотек. С этих пор вы просто начнёте «промисифицировать» все штуки, которые делаете, взамен на спокойные выходные.
Всё это заставляет сказать, что как-будто экосистема Node постоянно движется. И вектор этого движения не очень хорош. Новые инструменты, которые вроде как «намного круче» старых, будут встречаться каждый день. Только представьте: всегда можно найти то, что у вас есть, но только еще и «светится». Вы будете удивлены как просто это может случиться с вами. И похоже, сообщество это только поощряет. Вы используете Grunt!? Все используют Gulp!? Подождите минутку, используйте NPM нативные скрипты!
Пакеты, которые состоят из тривиального кода не более чем на 10 строк, загружаются тысячи раз каждый день из NPM. Вы серьезно? Вам правда нужно установить всю эту вереницу зависимостей, чтобы просто проверить тип массива? И эти самые пакеты используются такими гигантами как React и Babel.
Вы никогда не станете мастером того, что движется с такой, ломающей голову, скоростью. И это ни слова не говоря о «стабильности» таких движений.
Если вы пришли из другого языка, такого как Python, Ruby или PHP, то вы будете ожидать, что что-то бросит вам исключение, что-то его поймает, на худой конец функция вернет ошибку. И это нормально. Совершенно нормально… Но в Node это не так. Наоборот, вы передаете ошибки в ваши callback'и или promis'ы — всё верно, никаких бросков исключений. Но это работает ровно до тех пор, пока вы не захотите получить стек вызовов у нескольких вложенных callback'ов. Не говоря уже о том, что если вы забудете вернуть callback в случае ошибки, то скрипт продолжит выполняться и вызовет набор других ошибок, в добавок к первой. Вам придется удвоить количество отладочной информации, чтобы нормально дебажить.
Даже если вы начнете «серьезно» по стандарту обрабатывать свои собственные ошибки, вы не можете (без чтения исходников) убедиться, что большая часть пакетов, установленных из NPM, используют тот же подход.
Эти проблемы приведут вас к использованию «catchall» обработчиков исключений. Которые смогут залогировать проблемное место и позволят вашему приложению не просто грациозно «упасть». Запомните, Node однопоточный. Если что-то заблокирует процесс, всё вокруг порушится к чертям. Но это круто, вы же используете Forever, Upstart and Monit, верно?
Чтобы управлять 'callback hell', ошибками и трудночитаемой логикой всё больше и больше разработчиков начинают использовать Promis'ы. Это способ писать код, который выглядит более-менее синхронно, без сумасшедшей 'callback' логики. К сожалению, не существует ни одного «стандарта» (как и для ничего другого в JavaScript'е) для реализации или использования Promis'ов.
Самая часто упоминаемая библиотека сейчас — это Bluebird. Она довольно хороша, быстро работает и заставляет вещи «просто работать». Как бы там ни было, я нашел очень полезным оборачивать всё, что мне нужно в Promise.promisifyAll().
По большей части я использовал замечательную библиотеку async, чтобы держать свои callback'и под контролем. С ней всё выглядело более естественно.
Ближе к концу моего опыта с Node генераторы стали более популярны. Я так и не закончил свое «погружение» в них и поэтому не могу толком ничего сказать. Хотелось бы услышать кого-нибудь, кто с ними знаком поближе.
Последней каплей было то, что я обнаружил отсутствие стандартов. Такое ощущение, как будто каждый имеет свое собственное представление о том, как работать с вещами, описанными выше. Callback'и? Promis'ы? Обработка ошибок? Build скрипты? Да этому нет конца.
Это всё накаляет… Никто не может сказать, как написать стандартизированный JavaScript-код. Просто забейте в гугле «JavaScript Coding Standards» и вы поймете, что я имею ввиду.
Я понимаю, что много языков не имеют строгой структуры, но они обычно имеют стандартный гайдлайн, созданный мейнтейнерами языка.
Единственное, что хоть как-то приемлемо, написали в Mozilla.
Я потратил год, пытаясь заставить JavaScript и более специфичный Node работать на нашу команду. К сожалению, за это время мы потратили больше часов на поиск документации, знакомство со «стандартами», споры о библиотеках и отладку тривиального кода, чем на что-то полезное.
Посоветовал бы я Node для больших проектов? Абсолютно нет. Будут ли люди использовать его всё равно? Конечно будут. Я тоже пытался.
Как бы там ни было, я бы рекомендовал JavaScript для фронтенд разработчиков, таких как Angular или React (как будто у вас есть другой выбор).
Так же бы я посоветовал Node для простых back-end серверов, используемых в основном для вебсокетов или предоставления API. Это можно просто сделать с Express, говорю так, потому что мы так и сделали для своего Quoterobot PDF-процессинг сервера. Это один файл, содержащий 186 строк кода, включая пробелы и комментарии. И он так же хорошо работает, насколько он прост.
Вполне возможно вы удивлены, что я делаю сейчас? Сейчас я продолжаю писать главные части наших продуктов и API, используя Python. В основном на Flask или Django, используя либо Postgres либо MongoDb.
Это проверенный временем вариант с великолепными стандартами, библиотеками, легкой отладкой и стабильной работой. Конечно, и у него есть недочеты. Но он хорош, стоит только начать писать на нем. По некоторой причине Node привлек внимание моих глаз и затянул меня. Я не сожалею о своей попытке подружиться с ним, но чувствую, что потратил на него больше времени, чем должен был.
Я надеюсь, JavaScript и Node улучшат в будущем. Я буду счастлив попробовать его вновь.
Расскажите о своем опыте? Были ли у вас проблемы, которые я испытывал? Закончили ли вы «перескакивать» назад, на более комфортный язык?
Год разработки с штатными инструментами командной строки, клиентские проекты и выпуск обновлений для продуктов нашей компании, в двух словах, это то, чему я научился за это время. Я бы хотел поделиться опытом, но речь пойдет не столько про Node, сколько за весь JavaScript в целом.
Легко научиться, но стать мастером невозможно
Node очень прост для освоения. Особенное если вы знакомы с JavaScript. Нагуглите несколько туториалов для начинающих, поиграйтесь с Express и вы уже в теме, не так ли? Затем вы начнете осознавать, что нужно как-то взаимодействовать с базами данных. Нет проблем, запускаете NPM… Ага, всего лишь горстка приличных SQL пакетов. Позднее вы поймете, что все существующие ORM инструменты никуда не годятся и то, что базовый драйвер — это лучший выбор. Теперь у вас проблемы c реализацией своей модели и проверкой логики. Скоро после этого, вы начнете писать более сложные запросы и просто потеряетесь в callback'ах. Естественно, вы прочитаете про 'callback hell', плюнете на это дело и начнете использовать одну из многих promise библиотек. С этих пор вы просто начнёте «промисифицировать» все штуки, которые делаете, взамен на спокойные выходные.
Всё это заставляет сказать, что как-будто экосистема Node постоянно движется. И вектор этого движения не очень хорош. Новые инструменты, которые вроде как «намного круче» старых, будут встречаться каждый день. Только представьте: всегда можно найти то, что у вас есть, но только еще и «светится». Вы будете удивлены как просто это может случиться с вами. И похоже, сообщество это только поощряет. Вы используете Grunt!? Все используют Gulp!? Подождите минутку, используйте NPM нативные скрипты!
Пакеты, которые состоят из тривиального кода не более чем на 10 строк, загружаются тысячи раз каждый день из NPM. Вы серьезно? Вам правда нужно установить всю эту вереницу зависимостей, чтобы просто проверить тип массива? И эти самые пакеты используются такими гигантами как React и Babel.
Вы никогда не станете мастером того, что движется с такой, ломающей голову, скоростью. И это ни слова не говоря о «стабильности» таких движений.
Обработка ошибок по принципу «повезло\не повезло»
Если вы пришли из другого языка, такого как Python, Ruby или PHP, то вы будете ожидать, что что-то бросит вам исключение, что-то его поймает, на худой конец функция вернет ошибку. И это нормально. Совершенно нормально… Но в Node это не так. Наоборот, вы передаете ошибки в ваши callback'и или promis'ы — всё верно, никаких бросков исключений. Но это работает ровно до тех пор, пока вы не захотите получить стек вызовов у нескольких вложенных callback'ов. Не говоря уже о том, что если вы забудете вернуть callback в случае ошибки, то скрипт продолжит выполняться и вызовет набор других ошибок, в добавок к первой. Вам придется удвоить количество отладочной информации, чтобы нормально дебажить.
Даже если вы начнете «серьезно» по стандарту обрабатывать свои собственные ошибки, вы не можете (без чтения исходников) убедиться, что большая часть пакетов, установленных из NPM, используют тот же подход.
Эти проблемы приведут вас к использованию «catchall» обработчиков исключений. Которые смогут залогировать проблемное место и позволят вашему приложению не просто грациозно «упасть». Запомните, Node однопоточный. Если что-то заблокирует процесс, всё вокруг порушится к чертям. Но это круто, вы же используете Forever, Upstart and Monit, верно?
Callback, Promise или Generator!?
Чтобы управлять 'callback hell', ошибками и трудночитаемой логикой всё больше и больше разработчиков начинают использовать Promis'ы. Это способ писать код, который выглядит более-менее синхронно, без сумасшедшей 'callback' логики. К сожалению, не существует ни одного «стандарта» (как и для ничего другого в JavaScript'е) для реализации или использования Promis'ов.
Самая часто упоминаемая библиотека сейчас — это Bluebird. Она довольно хороша, быстро работает и заставляет вещи «просто работать». Как бы там ни было, я нашел очень полезным оборачивать всё, что мне нужно в Promise.promisifyAll().
По большей части я использовал замечательную библиотеку async, чтобы держать свои callback'и под контролем. С ней всё выглядело более естественно.
Ближе к концу моего опыта с Node генераторы стали более популярны. Я так и не закончил свое «погружение» в них и поэтому не могу толком ничего сказать. Хотелось бы услышать кого-нибудь, кто с ними знаком поближе.
Плохая стандартизация
Последней каплей было то, что я обнаружил отсутствие стандартов. Такое ощущение, как будто каждый имеет свое собственное представление о том, как работать с вещами, описанными выше. Callback'и? Promis'ы? Обработка ошибок? Build скрипты? Да этому нет конца.
Это всё накаляет… Никто не может сказать, как написать стандартизированный JavaScript-код. Просто забейте в гугле «JavaScript Coding Standards» и вы поймете, что я имею ввиду.
Я понимаю, что много языков не имеют строгой структуры, но они обычно имеют стандартный гайдлайн, созданный мейнтейнерами языка.
Единственное, что хоть как-то приемлемо, написали в Mozilla.
Итоги по Node
Я потратил год, пытаясь заставить JavaScript и более специфичный Node работать на нашу команду. К сожалению, за это время мы потратили больше часов на поиск документации, знакомство со «стандартами», споры о библиотеках и отладку тривиального кода, чем на что-то полезное.
Посоветовал бы я Node для больших проектов? Абсолютно нет. Будут ли люди использовать его всё равно? Конечно будут. Я тоже пытался.
Как бы там ни было, я бы рекомендовал JavaScript для фронтенд разработчиков, таких как Angular или React (как будто у вас есть другой выбор).
Так же бы я посоветовал Node для простых back-end серверов, используемых в основном для вебсокетов или предоставления API. Это можно просто сделать с Express, говорю так, потому что мы так и сделали для своего Quoterobot PDF-процессинг сервера. Это один файл, содержащий 186 строк кода, включая пробелы и комментарии. И он так же хорошо работает, насколько он прост.
Возвращение на Python
Вполне возможно вы удивлены, что я делаю сейчас? Сейчас я продолжаю писать главные части наших продуктов и API, используя Python. В основном на Flask или Django, используя либо Postgres либо MongoDb.
Это проверенный временем вариант с великолепными стандартами, библиотеками, легкой отладкой и стабильной работой. Конечно, и у него есть недочеты. Но он хорош, стоит только начать писать на нем. По некоторой причине Node привлек внимание моих глаз и затянул меня. Я не сожалею о своей попытке подружиться с ним, но чувствую, что потратил на него больше времени, чем должен был.
Я надеюсь, JavaScript и Node улучшат в будущем. Я буду счастлив попробовать его вновь.
Расскажите о своем опыте? Были ли у вас проблемы, которые я испытывал? Закончили ли вы «перескакивать» назад, на более комфортный язык?