Size: a a a

Жабаскрипт

2019 March 03
Жабаскрипт
Я время от времени делаю доклады про архитектуру веб-приложений. И там всегда есть 2 важных пункта:

1. Архитектура вашего приложения не должна зависеть от веб-фреймворка. Нужно писать код так, чтобы вы могли заменить веб-фреймворк (скорее всего вы делать это не будете, но это хорошая мысленная проверка на связанность).
2. Model (из MVC) - это ваша бизнес-логика и логика приложения. Model может быть реализован совершенно разными способами. Чаще всего у нас это Services(use cases) + Domain Model. И слой Model не зависит от Controller и View.

Если все сделано правильно, то вы сможете без проблем заменить JSON в вашем REST API на XML, и вам нужно будет только обновить контроллеры. Или даже больше, вы меняете веб-интерефейс на интерфейс командной строки и вам не нужно переписывать бизнес-логику, поскольку все смещенно в слой Model.

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

И если у вас все хорошо с архитектурой, то вы не работаете с базой данных напрямую. Вы подключаете Domain Model или нужные сервисы в ваши скрипты и работаете уже с ними. Это обеспечивает целостность ваших данных. Кроме того, для многих операций в будущем может появится Web-интерфейс.

API ваших скриптов - это аргументы командной строки, их нужно парсить. Поскольку в WebbyLab мы используем не только NodeJs, то для парсинга аргументов нам зашел http://docopt.org/ Есть реализация под различные языки (есть docopt модуль и на npm). Основная идея - вы пишите справку по использованию, а docopt автоматически парсит аргументы командной строки в соотвествии со справкой.

Отличный пример на сайте docopt, копируете его и правите под себя :)

Naval Fate.

Usage:
 naval_fate ship new <name>...
 naval_fate ship <name> move <x> <y> [--speed=<kn>]
 naval_fate ship shoot <x> <y>
 naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
 naval_fate -h | --help
 naval_fate --version

Options:
 -h --help     Show this screen.
 --version     Show version.
 --speed=<kn>  Speed in knots [default: 10].
 --moored      Moored (anchored) mine.
 --drifting    Drifting mine.


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

Но совсем недавно мне скинули неплохой тьюториал для новичков про то, как оформить ваше CLI приложение в npm пакет "A guide to creating a NodeJS command-line package"

Он будет хорошим дополнением ко всему вышесказанному.

Основные моменты статьи:
1. Не забываете про шебанг #!/usr/bin/env node
2. Делайте файл исполняемым chmod +x cli.js
3. Добавьте секцию bin в ваш package.json. Например, bin: {myapp: './cli.js'}. Тогда при установке вашего пакета npm создаст в директории с бинарями симлинк "myapp", который будет указывать на ваш "cli.js".
4. npm link линкует не только пакеты, но и скрипты с секции bin. Этого я сам не знал :)
источник
2019 March 04
Жабаскрипт
Сегодня хочу поделиться отличной статьей от одного из моих коллег из WebbyLab. Статья про JavaScript, машинное обучение и Игру Престолов 😁

Не так давно мы делали интересный проект связанный и интелектуальным поиском по медицинским данным. Основная задача - поиск по смыслу (не путайте с полнотекстовым поиском). Например, если мы ищем "рак", то система должна находить "меланома". Мы можем искать по аналогиям вида, для лечения болезни "А" используется "Б", что тогда используется для лечени болезни "C". Чтобы это реализовать, для каждого слова необходимо построить некоторое представление, которое хранит его смысл (смысл определяется словами вокруг целевого слова и называется контекстом). Таким представлением выступает вектор в N-мерном пространстве (обычно от 100 до 300). Имея такие вектора для каждого слова, можно делать математические операция над векторами, что позволяет искать похожие слова по смыслу и тд. Это называется word embeddings и есть различые модели построения векторов (word2vec, GloVe, fastText и тд). Все детали есть в статье.

Но для того, чтобы не было скучно, автор решил обучать модели текстами романа "Игра Престолов" и искать связи между героями 🙈. Что из этого получилось - найдете в статье.

Кроме того, ты сможешь повторить все это самостоятельно с любым текстом (код прилагается).

Статья на Medium: https://medium.com/@WebbyLab/how-to-train-word2vec-model-with-nodejs-in-5-steps-41b602080c1a

Подписывайтесь на наш аккаунт на Medium :)
источник
2019 March 10
Жабаскрипт
Хотел поделиться забавным опытом использования JavaScript. Я бы назвал это: "Лямбды на шару в Google документах" 😁

Всегда хочется расширить границы для JavaScript разработчика. Можно делать декстопные приложения, мобильные приложение, можно обучать нейронки при помощи Tensoflow.js и много всего другого. Но мало кто знает, что есть Google Apps Script и это очень простой в использовании и мощный инструмент, и пишити вы скрипты на JavaScript :)

Изначальная задача: пользователь заполняет форму регистрации на ивент, а потом оплачивает услугу. У каждого пользователя своя ссылка для оплаты услуги, поскольку в ссылку зашито описание с именем (нужно, чтобы потом найти платеж в LiqPay), сумма и тд.

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

Шаги:
1. Создаю Google форму регистрации на ивент и в меню выбираю пункт "Script Editor"
2. Разбираюсь, как LiqPay генерирует ссылки (механизм подписи и формат данных) и реализовываю свою версию.
3. Пишу код, который берет с формы email пользователя и генерирует ссылку.
4. Вешаю на onSubmit отправку ссылки для пользователю на его email. Самое крутое, что ничего не нужно настраивать, письмо уйдет через твой Gmail акк.

Но можно пойти дальше. Скрипт можно опубликовать, как add-on. Дописать на HTML/CSS UI для выбора поля из которого брать email, указать сумму для оплаты и тд. И такой add-on можно подключать в любую форму.

Но интереснее даже другое, как использовать Google Docs для своего HTTP бэкенда (по типу serverless). И это тоже можно :)

Пример с LiqPay:
1. Переходим на https://script.google.com/ и выбираем "New Script".
2. Создаем функцию "doGet" ("doPost" тоже поддерживается) и в этой функции описываем логику, которая должна отработать когда пришел к нам запрос. Мой код, только нужны LiqPay ключи.
3. Выбираем "Deploy as web app" в меню "Publish"
4. Используем полученную ссылку как эндпоинт для нашего бэкенда :)

Протестировать можно тут - https://script.google.com/macros/s/AKfycbzGo5SXnnH_CwxQgbKYI88oGuyq8Eh7UBqfTeB2x5S6iEdlA0jS/exec?description=ЖабеНаЕду (можете менять описание на любое другое и бэкенд будет генерировать новую подписанную ссылку для LiqPay)

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

Вот классная статья, которая описывает, как сделать свою serverless CMS на базе "Google Apps Script".В качестве админки для добавления постов - Google Forms, хранилище данных - Google Spreadsheets. Весь фронт на HTML/CSS и общается с "лямбдами" гугла. Есть пагинация, категории, все по AJAX.

Да, вот такими глупостями занимаюсь на выходных 😁
источник
2019 March 11
Жабаскрипт
"Скорость света и Веб - часть 3".

Если вы не читали первые 2 части, то найдите их в истории канала. Стоит начать с них.

Мы закончили на том, что даже при канале в 100Мбит/c, нужно более 600мс (а не 4мс), чтобы получить 50КБ данных при первом подключении, если клиент в Киеве, а сервер в Сан-Франциско.

Хотел перейти к решениям, но есть еще одна проблема про которую все-таки стоит сказать - "Head-of-line Blocking". На самом деле, когда говорят про "Head-of-Line Blocking", то могут подразумевать разное. Есть 2 варианта этой проблемы:

1️⃣ "Head-of-line Blocking" на уровне TCP

Мы рассмотрели ситуацию, когда у нас нет потерь пакетов, но на практике пакеты всегда теряются. Более того, TCP Slow Start увеличивает скорость пока не начнут теряться пакеты, потом значительно уменьшает скорость и начинает поднимать более медленно.

Потери пакетов могут приводить к "Head-of-line Blocking" на TCP уровне.
Попробую описать основную идею.
TCP отвечает за то, чтобы пакеты пришли в приложение в правильном порядке. Если сервер отправил:
[1][2][3][4][5]
а получили мы (или в другом порядке)
[2][3][4][5]
То эти пакеты находятся в TCP буфере получателя пока сервер отправляет нам повторно пакет [1]. То есть, задача TCP протокола выстроить пакеты в правильную очередь перед тем, как они попадут в приложение. Это удобно, но далеко не всегда нужно.

2️⃣ "Head-of-line Blocking" на уровне HTTP/1.x

Тут немного другая ситуация.
Допустим, нам нужно сделать 10 HTTP запросов. Браузер отправляет запросы один за одним и получается, чтобы отправить новый, он должен дождаться результата предыдущего.

Схематически это выглядит так:

50ms: Kyiv ------запрос 1----> SF
20ms: работа бэкенда (запрос 1)
50ms: Kyiv <-----ответ 1------ SF
50ms: Kyiv ------запрос 2----> SF
20ms: работа бэкенда (запрос 2)
50ms: Kyiv <-----ответ 2------ SF
50ms: Kyiv ------запрос 3----> SF
20ms: работа бэкенда (запрос 3)
50ms: Kyiv <-----ответ 3------ SF

Для упрощения я упустил все моменты связанные с установкой соединения (TCP-handshake, TLS-handshake, TCP Slow Start).

В связи с этим, в HTTP/1.1 появился "HTTP Pipelining". Суть - отправить сразу пачку запросов и ждать ответы.
"HTTP Pipelining" выглядит так:

50ms: Kyiv ------запрос 1----> SF
     Kyiv ------запрос 2----> SF
     Kyiv ------запрос 3----> SF
20ms: работа бэкенда (запрос 1)
     работа бэкенда (запрос 2)
     работа бэкенда (запрос 3)
50ms: Kyiv <-----ответ 1------ SF
     Kyiv <-----ответ 2------ SF
     Kyiv <-----ответ 3------ SF

Это полезная штука (120мс против 360мс), но на практике она отключена в большинстве браузеров ввиду того, что реализации серверов часто содержат баги. Но даже, если бы это работало, все равно мы имеем проблему "Head of line blocking": если обработка первого запроса займет 1 секунду, то ответы не смогут вернуться раньше чем через секунду (поскольку первый запрос блокирует возврат остальных).

Да, браузер может параллельно открывать 4-6 соединений (это с настройками по умолчания), но это лишь частично спасает ситуацию.

Проблемы с DNS. Я не обсуждал запросы к DNS ввиду:
✳️ В 99% случаев для DNS используется UDP (за редкими исключением, когда ответ не влазит в датаграмму, тогда может быть инициировано TCP соединение). То есть нам почти никогда не нужна установка соединения, что сильно уменьшает нашу проблему. Вопросы безопасности пока упустим.
✳️ Скорее всего, мы обращаемся к DNS серверу провайдера и сервер этот находится достаточно близко. Да, это все равно отдельный запрос, который тоже влияет на то, как быстро пользователь увидит страницу, но в детали пока вдаваться не будем.

Теперь точно можно переходить к обсуждению того, как с этим всем жить и что делать дальше.

Продолжение следует...
источник
2019 March 24
Жабаскрипт
Как мы управляем конфигами для 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
источник
2019 April 07
Жабаскрипт
Рабочая архитектура веб-приложений.

Не так давно прошла конференция для архитекторов 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
И масса других вопросов остаются открытыми (локализация, конфигурации и тд).

В очередной раз думаю, что нужно заопенсорсить наш бойлерплейт, но основной момент - это подготовить документацию и описать идеи, которые стоят за ним. Видео доклада - первый шаг в эту сторону 🙂
источник
2019 April 25
Жабаскрипт
Мое утро началось с просмотра исключительно интересного доклада "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
источник
2019 May 12
Жабаскрипт
Наткнулся на интересную статью на Хабре "Нужно ли чистить строки в 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).
источник
2019 May 14
Жабаскрипт
Мой коллега Рома Пухлий решил сделал полезное дело и написал статью про Linux утилиты, которые следует знать каждому разработчику - "Linux utilities that every developer should know"

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

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

К этому всему есть удобная шпаргалка в PDF https://blog.webbylab.com/images/linux-print.pdf
Большинство команд должно работать и на OSX.
источник
2019 May 20
Жабаскрипт
Вот прошел 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 имеет самый быстрый райнтайм среди динамических языков.
источник
2019 May 22
Жабаскрипт
Еще один доклад с 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 и различные опции.
источник
2019 May 27
Жабаскрипт
Меня сняли на скрытую камеру 😁

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

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

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

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

Обновляемый список моих докладов: https://github.com/koorchik/my-talks
источник
2019 June 05
Жабаскрипт
Сейчас мы делаем несколько проектов для автоматизации умного дома. Софт на 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 МБ на каждый новый образ
источник
2019 June 30
Жабаскрипт
Как работает Google со своим кодом или немного про монорепозитарии.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Но консистеность кода для нас очень важна:
✅ Мы используем единый подход к архитектуре на большинстве проектов (описывал тут https://t.me/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!
источник
2019 July 05
Жабаскрипт
И еще немного про монолиты и микросервисы в продолжение https://t.me/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
источник
2019 July 18
Жабаскрипт
Как я переезжал с 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


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

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

Как сабмитить результаты еще думаю 🙂
источник