Жабаскрипт (веде Віктор Турський)
4.62K subscribers
22 photos
2 videos
273 links
Авторський контент для JavaScript розробників, але не завжди про JS:). Пишу про архітектуру, best practices, продуктивність, безпеку, інструментарій.

Viktor Turskyi (@koorchik), Cofounder at Webbylab, SWE at Google

Рекламу не розміщую!
Download Telegram
Как мы управляем конфигами для NodeJs приложений?

Решил поделиться, как мы управляем конфигами для NodeJS приложений.
Давайте отталкиваться от того, что у нас есть несколько окружений, к примеру "prod", "staging", "qa".

Как не стоит управлять конфигурацией?
Я часто видел решения, когда в репозитарии есть config-prod.json, config-staging.json, config-qa.json. Рекомендую такого подхода избегать.

Он имеет массу недостатков:

1. Разграничение доступа. Все разработчики, которые имеют доступ к коду не обязательно должны иметь доступ к продакшену. Также такой код нельзя выложить в OpenSource ввиду наличия в репозитории конфигов с параметрами доступа к вашим окружениям.
2. Набор окружений может расширяться: различные дев-боксы, локальное окружение и тд. Это будет тяжело поддерживать. Вам постоянно придется добавлять новые файлы репозиторий и поддерживать много копий конфигов.
3. Создавая билды в виде docker-контейнеров, они будут привязаны к окружению и это нарушает главную идею использования билдов. По правильному было бы протестировать билд на QA, а потом выкатить этот же билд в prod, а не собирать новый. Сборка нового повышает риск, что у нас будет другая конфигурация (например, обновился один из зависимых пакетов).

Подход "Двенадцать факторов" и почему его недостаточно

Если вы используете подход "Двенадцать факторов", то уже все это знаете. По принципу "Двенадцати факторов", конфигурация должна передаваться в переменных окружения - https://12factor.net/ru/config.

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

Но одновременно, передавать переменные окружения, при запуске процесса, не слишком удобно. Есть популярная библиотека dotenv, которая позволяет сохранять переменные окружения в файле .env, но вы не должны коммитить этот файл в репозиторий, обычно в репозитории вы держите файл с примером конфигурации (например ".env.sample", который нужно скопировать в .env файл, если вы деплоите без докера). Вы можете использовать dotenv-defaults, библиотека, которая позволяет вам иметь файл .env.defaults со значениями по умолчанию, который вы уже коммитите в репозиторий.

Но в реальности и этого недостаточно. У вас будут достаточно сложные конфиги с вложенностями и и вы часто не хотите определять все значения в переменных окружения, их слишком много и не все из их изменяются в зависимости от окружения (например ссылка на ваш аккаунт вашего продукта в твиттере должна быть в конфиге, но ее нет смысла передавать в переменных окружения). Если вы знакомы с ansible, то знаете, что можно иметь шаблон конфига и подставлять в него переменные в зависимости от окружения и собирать финальный конфиг, но вы помните, что вы не можете паковать этот конфиг в билд. В связи с этим, мы храним шаблон конфига в репозитории и шаблонизируем его переменными окружения. Также, если в конфиге много опций, то имеет смысл автоматически валидировать его корректность после шаблонизации, это сильно облегчит жизнь разработчикам.
Как мы управляем конфигами для NodeJs приложений? (продолжение предыдущего поста)

Теперь к делу, вместо того, чтобы просто рассказывать про проблемы, я посидел ночь и напилил confme - решение, которое идет в наш бойлерплейт по NodeJS и может быть полезно комьюнити :)

confme совсем небольшая библиотека, но решает все вышеописанные проблемы, это небольшая обертка вокруг dotenv-defaults и LIVR (для валидации).

Что делает confme?

"confme" загружает конфиг и шаблонизирует его переменными окружения. Для получения переменных окружения библиотека использует dotenv-defaults, то есть вы можете создать файл ".env.defaults" (если захотите) для сохранения значений по умалчанию ваших переменных окружения. Если в конфиге у вас есть плейсхолдеры для которых не существует переменных окружения, то "confme" выбросить исключение.

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

Как использовать?

const confme = require("confme");
const config = confme( __dirname + "/config.json" );

Файл с конфигурацией:

{
"listenPort": "{{PORT}}",
"apiPath": "https://{{DOMAIN}}:{{PORT}}/api/v1",
"staticUrl": "https://{{DOMAIN}}:{{PORT}}/static",
"mainPage": "https://{{DOMAIN}}:{{PORT}}",
"mail": {
"from": "MyApp",
"transport": "SMTP",
"auth": {
"user": "{{SMTP_USER}}",
"pass": "{{SMTP_PASS}}"
}
}
}

DOMAIN, PORT, SMTP_PASS, SMTP_PASS нужно передать в переменных окружения любым способом:

Я рекомендую поступать следующим образом:

☑️ Значения по умолчанию для переменных окружения определите в файле ".env.defaults". Они должны подходить для локальной разработки. То есть, разработчику после клонирования репозитория не нужно будет ничего настраивать.
☑️ Если вы деплоите без контейнеров и лямбд, то можете во время деплоймента создавать файл ".env" и переопределять переменные по умолчанию для вашего окружения. Либо можно передавать при запуске, если у вас всего несколько параметров (правда есть риск забыть про них при перезапуске или дебаге продакшена): DOMAIN=myapp.com PORT=80 node app.js.
☑️ Если вы собираете докер контейнер или пакуете лямбды, то просто передавайте переменные окружения способом, который подходит для вашего окружения (для этого всегда есть механизм).

"confme" из коробки поддерживает валидацию конфига при помощи LIVR. Использует LIVR JS и livr-extra-rules (набор дополнительных правил)

Вы можете передать путь к файлу со схемой конфига вторым аргументом:

const confme = require("confme");

const config = confme(
__dirname + "/config.json",
__dirname + "/config-schema.json"
);

Вот пример схемы:

{
"listenPort": ["required", "positive_integer"],
"apiPath": ["required", "url"],
"staticUrl": ["required", "url"],
"mainPage": ["required", "url"],
"mail": ["required", {"nested_object": {
"from": ["required", "string"],
"transport": ["required", {"one_of": ["SMTP", "SENDMAIL"] }],
"auth": {"nested_object": {
"user": ["required", "string"],
"pass": ["required", "string"]
}}
}}]
}

Буду рад получить фидбек от читателей канала. Можно в виде звездочек на гитхабе или пишите мне в ТГ. Я - @koorchik
Рабочая архитектура веб-приложений.

Не так давно прошла конференция для архитекторов devpoint.ua. Я делился тем, как мы строим архитектуру веб-приложений у нас в компании.

Архитектура очень схожа на "Clean Architecture" Роберта Мартина. Про "Clean Architecture" многие говорят, но это достаточно абстрактная концепция и от деталей зависит многое, я постарался рассказать, как мы на практике ее используем. Эта архитектура у нас работает на NodeJs, Perl, PHP проектах совершенно разных масштабов (более 60 проектов). Первый проект на этой архитектуре мы построили 7 лет назад, за это время архитектура эволюционировала, но ядро и концепции не поменялись за все годы.

Когда мы внедрили свою архитектуру, то понятия "Clean Architecture" еще не было, мы все строили опираясь на здравый смысл, а потом через какое-то время нашли этому всему правильные названия. На самом деле, вся история про различные шаблоны проектирования именно про названия. То есть это не сокральное знание, а что-то, что люди часто используют и дали ему имя. Зачастую шаблон проектирования это не определение "лучшего решение" (или даже не Best Practices), а просто решение, которое часто работает в определенной ситуации. Думаю, стоит написать отдельный философский пост посвященный моему видению различных архитектурных шаблонов 🙂

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

ВИДЕО ДОКЛАДА: https://www.youtube.com/watch?v=TjvIEgBCxZo

Обещаю ответить на все вопросы в комментариях под видео (если такие будут) 🙂

На самом деле, очень большая проблема найти адекватный бойлерплейт для проектов на NodeJs. Возьмите тот же NodeJs бойлерплейт от Microsoft. Несмотря на 4736 звезд, это не то, что я был взял себе в проект. Из очевидных моментов:
Не хватает слоя сервисов (юз-кейсов) и в результате у нас нет нормальной изоляции от HTTP состовляющей и формата передачи данных.
Не хватает надежного подхода к валидации. То есть, разработчик может смело пропустить валидацию и не ясно, что делать с иерархическими структурами.
Бойлерплейт весь на колбеках (continuation passing style). У нас уже давно есть промисы, и даже более - async/await
И масса других вопросов остаются открытыми (локализация, конфигурации и тд).

В очередной раз думаю, что нужно заопенсорсить наш бойлерплейт, но основной момент - это подготовить документацию и описать идеи, которые стоят за ним. Видео доклада - первый шаг в эту сторону 🙂
👍1
Мое утро началось с просмотра исключительно интересного доклада "Rich Harris - Rethinking reactivity"

Что будет, если фреймворк превратить в компилятор? Получится svelte.

Svelte - библиотека, которая очень похожа на React, но:
Не имеет VirtualDOM, вся реактивность добавляется во время компиляции.
Компоненты получаются компактнее чем в React (исходный текст компонентов).
Svelte в 35 раз быстрее чем React и в 50 раз быстрее чем Vue.
Имеет поддержку CSS изоляции, анимаций и много другого, но это не увеличивает размер вашего бандла, если вы не это не используете. Это работает, поскольку у библиотеки нет рантайма (или почти нет) - все проиходит на этапе компиляции.
Имеется Sapper - аналог Next.js, но только для Svelte.

Не нужно сразу выбрасывать React/Vue/Angular/Ember и переходить на svelte, но точно стоит с этим поиграться, посмотреть на новые идеи.

ВИДЕО: https://www.youtube.com/watch?v=AdNJ3fydeao
Наткнулся на интересную статью на Хабре "Нужно ли чистить строки в JavaScript?"

В двух словах. Если у вас есть строка на 20мб, а вы вырезали из нее подстроку на 13 или больше символов, то v8 будет хранить ссылку на изначальну большую строку и не будет вычищать память.

Пример кода со статьи:

function MemoryLeak() {
// 15МБ строка
let huge = "x".repeat(15*1024*1024);

// Маленький кусочек
let small = huge.substr(0,25);
return small;
}

let arr = [];
setInterval(e=>{
let str = MemoryLeak();
arr.push(str);
console.log(JSON.stringify(arr).length, 'байт');
}, 500);

Я проверил на Node v12.2.0
Размер массива arr у меня был 3837 байт, а размер процесса дорос до 2ГБ и крашнулся с ошибкой
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

Такое поведение существует много лет, тикет был заведен еще в 2013 году.

Насколько это большая проблема? Если мы возьмем бэкенд, то он обычно не хранит состояние в памяти между запросами, и если пропадет ссылка на arr, то сборщик мусора все вычистит. Но вы можете столкнуться с данной проблемой, когда у вас есть кеши, которые живут между запросами, также на фронтенде, когда у вас есть долгоживущее состояние приложения (в SPA).
Мой коллега Рома Пухлий решил сделал полезное дело и написал статью про Linux утилиты, которые следует знать каждому разработчику - "Linux utilities that every developer should know"

Статья содержит примеры решения ежедневных задач при помощи командной стоки. Примеры поделены на разделы:

✳️ Работа с файлами
✳️ Работы с дисками
✳️ Чтение логов
✳️ Поиск по файловой системе
✳️ Работа с процессами
✳️ Запуск процессов в фоне
✳️ Использование системных ресурсов
✳️ Просмотр информации о системе
✳️ Сетевые утилиты
✳️ Другие утилиты

К этому всему есть удобная шпаргалка в PDF https://blog.webbylab.com/images/linux-print.pdf
Большинство команд должно работать и на OSX.
Вот прошел Google.io и я наткнулася на пару релевантных докладов. Один из них - What’s new in JavaScript (Google I/O ’19)

Я много лет в разработке, различные скрипты для управления сетевым оборудованием начал писать где-то в 2003. И практически с самого начала я писал на Perl и на JavaScript. Соответственно, я всегда сравниваю Perl и JavaScript. Я рад, что многие возможности, которые были в том же Perl много лет, появляются в JS.

Когда, я писал первый крупные SPA, то приходилось использовать ES3. С того момента JS кардинально поменялся:

✳️ В ES3 мне очень не хватало перловых "map" и "grep", пока не появились аналоги в JS - "map" и "filter".
✳️ Мне не хватало интерполяции переменных, как в Perl. В Perl каждая переменная начинается со знака $ и в двойных кавычках она интерполируется. То есть можно писать "Hello, ${name}!". Потом в JS появились Template Literals с аналогичным синтаксисом.
✳️ Переменные с лексической областью видимости - одна из фич, которые я прям ждал в JS. Даже до "let" и "const", я все равно объявлял все "var"-переменные , как если бы они имели локальную область видимости - максимально близко к месту их использования, такой код легче потом рефакторить.

JavaScript не варится сам в себе, а смотрит на соседние языки. Грамотно заимствует различные фичи, делая язык все более мощным.

Теперь по докладу:
1️⃣ Наконец-то в JS появился "string.matchAll". Кроме того, в stage 4 "RegExp Named Capture Groups" с синтаксисом как в Perl. Все это позволяет писать более понятный код. Вот пример регулярки на Perl. Еще немного и мы сможем писать так и в JS. Возможно даже грамматики подвезут :)
Главное не забывать слова Jamie Zawinski: "Некоторые люди, когда сталкиваются с проблемой, думают «Я знаю, я решу её с помощью регулярных выражений.» Теперь у них две проблемы"

2️⃣ Поддеркжа приватных свойств в классах. Похожая идея реализована в Perl6. Мне подход с # нравится значительно больше, чем через ключевое слово "private" (как в TypeScript и других языках), поскольку мы везде по коду можем видеть приватное или публичное свойство мы используем, да и объявление лаконичнее.

3️⃣ Разделители в числах. Можно писать 10_000_000, явно легче читается чем 10000000. Это есть во многих языках, включая Perl :)

4️⃣ array.flat и array.flatMap. В Perl я привык, что map может вернуть больше элементов, чем было в изначальном массиве, с flatMap наконец-то это тоже можно сделать и в JS :)

5️⃣ Object.fromEntries (противоположность Object.entries)

6️⃣ Поддержка BigInt из коробки.

7️⃣ Стабильная сортировка в стандарте. Кому-то это важно, я всегда писал код опираясь на идею, что сортировка нестабильная.

8️⃣ Promise.allSettled в дополнение к Promise.all и Promise.race.

9️⃣ Множество различных фич по i18n и l10n. Из наиболее полезных штук - Intl.RelativeTimeFormat.

Также в докладе показали улучшения производительности:
✳️ С node8 до node12 промисы стали в 11 раз быстрее. И что интересно, что нативный async/await в два раза быстрее чем нативные промисы.
✳️ С Chrome 61 до Chrome 65 на 40-100% ускорение скорости парсинга JS на реальных сайтах (типа FB).

На сегодняшний день, JavaScript имеет самый быстрый райнтайм среди динамических языков.
Еще один доклад с Google.io -
Beyond Mobile: Building Flutter Apps for iOS, Android, Chrome OS, and Web (Google I/O'19)

Flutter - это такой себе аналог React Native от Google, где вы пишите все на Dart. На самом деле, архитектура совершенно другая.
Flutter, в отличии React Native, реализует все с нуля. Все рендерится на канвасе через OpenGL и выглядит идентично на всех платформах.

Представьте себе, что вы пишете OpenGL игру, а потом запускаете ее на iOS и Android, она выглядит одинаково там и там, поскольку вы все нарисовали сами на канвасе. То есть, ваше Android приложение на iOS будет выглядеть точно также, как и на Android. Естественно, это не всегда хорошо и вы хотите нативное поведение для платформы. В связи с этим, Google реализовывает 2 набора компонентов, 2 набора механик/физики/анимаций под обе платформы и вы можете на Android увидеть как будет работать ваше iOS приложение и наоборот.

Flutter имеет отличную производительность (Google целится на 120fps, но пока это 60fps). На Android и iOS Dart код компилируется в ассемблерные инструкции для ARM архитектуры. У вас нет медленного JSCore, как в случае с React Native, и асинхронного "моста".

Сам же высокоуровневый API Flutter очень похож на то, что у нас есть в React, только батарейки в комплекте :).

В двух словах суть доклада в том, что Google запускает Flutter для десктопа и для веба:
✳️ https://flutter.dev/desktop
✳️ https://flutter.dev/web

Сейчас в WebbyLab мы смотрим на эту технологию, хотим сделать 1-2 небольших проекта. Один из проектов будет для комьюнити и уйдет в OpenSource. Я уже сам успел поиграться - Developer Experience отличный, рекомендую тоже посмотреть, если вам интересна мобильная разработка.

Начните с того, что установите себе Flutter Gallery и пощелкайте UI и различные опции.
👍1
Меня сняли на скрытую камеру 😁

Позавчера был в Днепре и должен был выступать на RunIT. Во время завтрака в отеле неожиданно встретил Максима Безуглого (основателя "Internation Software Architect Club"), который также должен был выступать на той же конференции. С Максом и Мишей Корбаковым у нас завязалась небольшая дискуссия про архитектуру, graphql, REST. Получился спонтанный выпуск "Архитектура и Кофе" 🙂

ВИДЕО (20 минут): https://www.youtube.com/watch?v=DSwpL3s_eZA

Также рекомендую к просмотру весь цикл "Архитектура и Кофе" (неформальные обсуждения вопросов архитектуры с опытными разработчиками и архитекторами) - https://www.youtube.com/playlist?list=PL9lOYXXTeW0oE-H1fXrJ5tdLLUkMtWJtC
Много лет назад наткнулся на пост Бобука (Григорий Бакунов, был директором по разработке в Яндексе, сейчас директор по распространению технологий) о том, как развивается эмоциональное состояние человека по мере его профессионального роста. Я этот текст перечитавал уже много раз. Ни убавить, ни прибавить.

Это стоит прочитать - "Как закаляется сталь"
https://github.com/bobuk/addmeto.cc/blob/master/pages/2013-04-19.md
Сделал страницу на Github со списком моих докладов на предстоящих конференциях и с материалами по прошедшим (контент все еще добавляю). Если вдруг тоже окажетесь на какой-то из этих конференций, буду рад пообщаться 🙂

Обновляемый список моих докладов: https://github.com/koorchik/my-talks
Сейчас мы делаем несколько проектов для автоматизации умного дома. Софт на NodeJs, запускается в докер-контейнерах на наших хабах. Хабы это железяки по типу raspberry/orange pi. Флеш память приходится сильно экономить и стал вопрос оптимизации размера докер образов.

Если взять стандартный образ node:10 и сделать все без оптимизаций, мы получим образ в 700МБ или больше. Базовый образ будет 600 МБ и 100 МБ слой с приложением.

Ребята из компании решили поделиться опытом и написали отличный пост про оптимизацию размера докер образов для NodeJS приложений.

Статья: Docker image size optimization for your Node.js app in 3 easy-to-use steps.

Результаты:
Размеры образов до оптимизации:
* 600 МБ базовый образ с NodeJS
* 100 МБ на каждый новый образ

Размеры образов после оптимизации:
* 100 МБ базовый образ с NodeJS
* 2.5 МБ на каждый новый образ
Как работает Google со своим кодом или немного про монорепозитарии.

Google использует монорепозитарий. Это значит:
Что весь код хранится в одном репозитории. Это несколько миллиардов строк кода. Размер репозитория 86 ТБ.
Это значит, что каждый сотрудник имеет сразу доступ ко всем проектам Google.
Все всегда вливается в HEAD (master/trunk).
Все зависимости также находятся в репозитории.

Мы привыкли к совершенно другому подходу. Спрашивается зачем это? Мы теряем всю гибкость, как в случае с множественными репозиториями. Но это выбор между консистентностью всего кода и гибкостью. Google выбирает консистеность.

Теперь вопросы и ответы:

Вопрос: Как это влияет на деплоймент?
Ответ: Деплоймент это независимая процедура и деплоить они могу свои микросервисы раздельно.

Вопрос: Получается у Google нет ревью кода?
Ответ: Ревью кода и тесты есть, и это ключевая часть процесса разработки. Просто, для этого есть отдельный инструмент/инфраструктура.

Вопрос: Как такой репозитория клонировать на локальную машину?
Ответ: Репозиторий не клонируется, а используется виртуальная файловая система.

Вопрос: Как происходит работа над Chromium, если это open source проекты?
Ответ: Chromium и Android не находятся в этой монорепе.

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

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

Вопрос: Кто еще использует монорепозитарий?
Ответ: Из того, что я знаю - это Facebook и Twitter

Вопрос: Нравится ли сотрудникам такой подход?
Ответ: Да, большинство сотрудников Google поддерживают такой подход.

В целом, преимущества монорепозитарий сродни преимуществам монолита - это выше консистеность кода.
Использование одного стиля (общий стайлгайд и правила статического анализа)
Зависимость от одной версии рантайма/языка.
Зависимости от одинаковых версий библиотек.

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

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

Но консистеность кода для нас очень важна:
Мы используем единый подход к архитектуре на большинстве проектов (описывал тут https://t.iss.one/jabascript/36)
Мы начинаем проекты с единого бойлерплейта.
Мы активно используем инструменты для статического анализа кода. Мы не просто используем eslint и его плагины по максимуму, мы еще имеем конфиг eslint на всю компанию. И ко всему этому, мы пишем еще свои плагины для eslint
4. Мы используем общий набор библиотек. К примеру для валидации мы создали LIVR, который работает одинаково в JavaScript, PHP, Perl (и на многих других платформах).

Если хотите знать больше про монорепозитарии, вот ряд интересных ссылок:
1. Why Google Stores Billions of Lines of Code in a Single Repository
2. Monolithic Repositories with Ciera Jaspan
3. Monorepos in the Wild
4. Monorepos: Please don’t!
И еще немного про монолиты и микросервисы в продолжение https://t.iss.one/jabascript/50.

Фейсбук это огромный монолит на PHP, раньше просто собирался 1.5 ГБ бинарь, которые просто деплоился на все сервера. Почему так? Неужели они не знают про микросервисы?

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

Но все это актуально не только для кода, но и для самой компании. Часто структура ваших микросервисов полностью соотносится со структурой команд в компании (вспомним тот же закон Конвея) . И это очень логично, когда каждая команда работает над своей частью (тут мы можем углубится в идеи code ownership, но не будем 🙂). Но по факту, по мере развития компании, в ней постоянно происходят изменения и мы заранее не знаем, какие они будут. Эти изменения ведут к реорганизации внутренней структуры компании и команд. В результате мы получаем, что в какой-то момент наша микросервисная структура не совпадает со структурой компании и это создает проблемы в поддержке.

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

Про координацию отлично сказал Пит Хант (помните парня, который показал миру React):

It's all about Amdahl's Law: you're going to be limited by how much coordination you need, and how much it costs.
Mono: coordination is cheap/free👍 But might encourage more coordination 👎
Poly: less required coordination👍 But if you need to coordinate, it's very expensive 👎

Это касается не только работы с репозиториями, но и вопроса монолит/микросервисы.

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

Ко всему этому могу порекомендовать послушать интервью с Питом Хантом - Facebook Engineering with Pete Hunt
Как я переезжал с Webpack на Parcel.

Для различных небольших демо я постоянно использую Parcel. Parcel - "zero configuration bundler" и это правда. Никаких конфигов, все работает из коробки и очень быстро. Если ты не пробовал Parcel, то обязательно попробуй.

Можно ли использовать Parcel в крупном проекте? Сложно ли переехать с Webpack на Parcel?
У меня выпала возможность это проверить.

Был у меня один проект для сборки которого использовался еще Webpack первой версии. И когда я начал обновлять Webpack и лоадеры, я понял, что проще написать все с нуля, чем исправлять существующую конфигурацию и зависимости. Если уже все равно все делать с нуля, то почему бы не подключить Parcel? 🙂

Что у меня было: Babel, JSX, ES6 modules, Less. Точкой входа был файл main.js.
Задача перевести все на Parcel с минимальным количеством плагинов и конфигурацией по умолчанию, с точкой входа index.html.

Делал я это в 2 этапа:
Этап 1: Перевести все на Parcel, но оставить точкой входа main.js
Этап 2: Поменять точку входа на index.html

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

Первый этап оказался сильно проще, чем второй и возникло всего 2 проблемы 🙂.
1️⃣ Перевести все на Parcel, но оставить точкой входа main.js

Проблема №1: В некоторых случаях перестал работать импорт функций (локализации и других утилит).

Этот код перестал работать, хотя до этого работал:

import { l } from '../i18n';


Проблема оказалась в том, что в модуле i18n экспорт выглядел вот так:

export default {
l(text) {
return jed.gettext(text);
}
};


Соответственно нужно было бы переписать на:

export function l(text) {
return jed.gettext(text);
};


но после этого перестал работать код вида:

import i18n from '../i18n';


Чтобы это завелось, пришлось привести модуль i18n к такому виду:

export function l(text) {
return jed.gettext(text);
};

export default {
l
};


По сути нужно было привести модули ES6 к корректному виду (их было 2-3 таких).

Проблема №2: Загрузка статики с неправильных путей.

В проекте была такая структура директорий для статики:
public/
index.html
static/
3d-party/
lang/
en.json
ru.json
uk.json
favicon.ico
build/
main.js
main.css
...


и соответственно пути в index.html выглядели так /static/build/main.js .
Все, что нужно было - это указать нужные аргументы командной строки для Parcel.

Финальная команда выглядела так:

parcel watch \
--out-dir publiс/static/build \
--public-url /static/build \
src/main.js


и после этого с точкой входа через main.js все заработало.

Осталось запустить http сервер для раздачи статики из папки public. Я использую для этого node-static (всегда установлен у меня глобально в систему).

cd public
// запустить в режиме SPA
// если запрашиваемого файла нет,
// то всегда возвращает index.html
static --spa -p 8090


Со вторым этапом все сложнее оказалось, опишу завтра 🙂
Forwarded from Viktor Turskyi
Небольшая заруба для всех участников конференции OdessaJS и подписчиков @jabascript, да и просто желающих. Играем в JavaScript Golf. Есть простая задача, нужно написать решение и чье решение будет содержать меньше всего символов, тот и победил.

Описание задачи - https://gist.github.com/koorchik/867460697526af0d84940960ee82652b
Правила тут (подключайтесь в чат) - @jsgolf

Как сабмитить результаты еще думаю 🙂
Долго думал нужен ли чатик для обсуждения постов. Я долго сомневался, но все таки пришел к выводу, что имеет смысл его сделать по нескольким причинам:
1️⃣ Возможность ответить на вопросы подписчиков или получить какие-то комментарии.
2️⃣ Определиться с темами, которые интересны подписчикам.
3️⃣ Иногда есть вещи, которые не сильно подходят для канала. Например, я часто выступаю на конференциях и постоянно кидать в канал, что на следующей неделе буду с таким-то докладом там-то вне сильно хочется. В канал планирую уже скидывать видеозаписи самых интересных докладов с какиме-то комментариями, но не анонс. А вот чатик для этого подходит 🙂.

Ссылка на чат - https://t.iss.one/jabascript_chat
Как я переезжал с Webpack на Parcel - часть 2

Лето оказалось перегружено поездками и конференциями. Наконец-то добрался до канала и решил опубликовать вторую часть про переезд на Parcel.

Первым этапом было перевести все на Parcel, но оставить точкой входа main.js.

Этап 2️⃣: Поменять точку входа на index.html и отказаться от лишних аргументов командной строки. Сборка должна выглядеть вот так
parcel index.html
и никаких конфигов.

Проблема №1: Перестали работать локальная статика типа jquery.js и пара других библиотек.

Parcel находит все теги script и собирает файлы в бандл(кроме файлов с внешних серверов). JQuery плагины перестали находить JQuery. Похоже, что проблема с тем, что модули уже объявляются не в корневом скоупе. Я долго не заморачивался, просто подключил все с CDN.

Проблема №2: перестали загружаться файлы локализации.
Для каждого языка в директории public/static/lang есть свой json файл. И был код, который загружал нужный файл локализации:

function loadLocaleData(locale) {
const url = `/static/lang/${locale}.json`;
return fetch(url);
}


В итоге, когда поменяли entry point на index.html, Parcel начал результат сборки помещать в dist. Естественно, он ничего не знает про статические файлы, которые не участвовали в сборке и loadLocaleData не может загрузить файлы локализации.

Тут есть несколько вариантов:
✳️ Использовать плагин parcel-plugin-static-files-copy (я решил не подключать лишний плагин).
✳️ Использовать динамический импорт (я пошел по этому пути).

По сути, я переписал код на такой:

const localeImports = {
ru: import(`../static/lang/ru.json`),
uk: import(`../static/lang/uk.json`)
};

export function loadLocaleData(locale) {
return localeImports[locale];
}


Может показаться, что я загружаю все файлы локализации сразу, но это не так. Import возвращает Promise, но загрузка файла начнется только, если вы запросите результат с промиса и загрузится только нужный файл. Подход с динамическим импортом лучше, чем c plugin-static-files-copy еще по нескольким причинам:
Файл будут минифицирован.
Имя файла будет содержать хеш сумму и я могу кешировать файл навечно (без проверки etag или last modified), что особенно актуально в данной ситуации (браузер не может показать интерфейс пользователю, пока не загружен файл локализации).

Отлично! Теперь у меня нет никаких конфигов для сборки и проект запускается одной простой командой
parcel index.html


Но нет 😬, осталась еще одна проблема - я не хочу, чтобы Parcel упаковывал конфиг приложения в билд. И на эту проблему я потратил больше всего времени.
Продолжение следует...
Как я переезжал с Webpack на Parcel - часть 3

✳️Часть 1
✳️Часть 2

Я обычно не пакую файл конфигурации в билд. Конфиг я загружаю таким образом:

<script type='text/javascript' src="/config.js"></script>

сам "/config.js" выглядит так:

window.APP_CONFIG = {
// my configuration here
};


и прячу его за модулем "src/config.js", чтобы код не ходил в глобальную переменную.

// src/config.js
export default window.APP_CONFIG;

такой подход позволяет использовать один и тот же билд в разных окружениях. Билд один, но для каждого окружения свой конфиг (по модели 12 factor app). Идея - выкатывать в прод только те билды, которые были протестированы.

И вот такое практически невозможно заставить работать в Parcel. Это оказался целый квест :)

То есть, если вы напишите:

<script type='text/javascript' src="/config.js"></script>
И вас нет файла config.js, то Parcel ругнется ошикой, а если config.js есть, то он попадет в бандл, что нас тоже не устраивает.

Я начал искать решение и оказалось, что на эту тему много issues (например, https://github.com/parcel-bundler/parcel/issues/144), но разработчики не добавляют такую фичу. Кто-то пытается писать дополнительные плагины для решения проблемы, но плагины оказались не сильно высокого качества.

Затем я попробовал posthtml с плагином "include", но в итоге все, что мы инклудим точно также процессится Parcel-ом.

Потом я подумал, почему не грузить динамически конфиг и написал такой код:

const script = document.createElement("script");
script.async = false;
script.type = "text/javascript";
script.src = "/config.js";
document.head.appendChild(script);

Но этот код не заработал, поскольку файл с конфигом выполнялся после main.js даже с опцией async=false.

В итоге, вот такое решение заработало:

<script type='text/javascript'>
document.write(`<${"script"} src="/config.js"> </${"script"}>`);
</script>


Что дальше? Сейчас для меня Parcel - это вариант по умолчанию, использую webpack только когда появляется необходимость в специфических фичах (например, для изоморфных приложений).

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

Надеюсь, что этот опыт будет полезен читателям канала 🙂
"NodeJS и OpenCV... однажды в Голливуде" - еще один интересный пост в блоге WebbyLab.
В одном из наших проектов возникла задача находить лицо человека на видео и замыливать его. Сегодня вся эта магия уже сугубо инженерная задача, просто нужно знать какие инструменты скомбинировать (OpenCV, ImageMagick, NodeJS, прямые руки). В продакшене это уже почти год и мы решили поделиться этим контентом с комьюнити. Возможно задача немного нестандартная для JS, но не только же CRUD клепать 🙂

ССЫЛКА НА ПОСТ: - https://medium.com/@WebbyLab/face-blurring-with-nodejs-and-opencv-once-upon-a-time-in-hollywood-f3b8e09669e8

Ну, и подписывайся на наш блог на Medium 🙂
Мы создаем еще одну IoT платформу для управления умным домом. Это еще один проект по автоматизации, который мы стартовали буквально пару месяцев назад.
В проекте: Docker, событийная модель работы, WebSockets, NodeJS, MQTT, много всяких интересных железяк, крутая команда.

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

➜ Сейчас расширяю команду в Киеве и есть возможность взять 2х людей (фултайм в офис). Нужны NodeJS разработчики уровня Junior+/Middle. Если у кого есть желание, то приходите к нам, всему научим, но думать и работать придется 🙂

Детальное описание вакансии тут - https://bit.ly/2muaYCW
Также мы регулярно обновляем список вакансий в канале @webbylab, подписывайтесь.

Если есть любые вопросы относительно вакансии, то пишите мне лично или в чат, или нашим рекрутерам на почту, или в @webbylab_recruiter.

ХАК: пишите, что узнали про вакансию на канале "Жабаскрипт", тогда на вас обратят внимание в первую очередь 🙂