Русский
Русский
English
Статистика
Реклама

Блог компании wrike

Фоторамка на Flutter своими руками

18.11.2020 12:16:11 | Автор: admin

Привет! Меня зовут Игорь, я работаю Frontend-директором в компании Wrike. В этой статье хочу поделиться историей создания моего пет-проекта и рассказать, как я сделал электронную фоторамку и написал для нее софт на Flutter, столкнувшись по пути со всеми возможными сложностями.

Я начал работу над проектом 24 апреля 2019 года. Почему я так хорошо помню дату? Потому что в этот день вышел обзор на IPS-матрицу с драйвером. Там было заявлено 8.9 дюймов и разрешение 2560*1600. Я подумал: Ничего себе! Девять дюймов и такая плотность пикселей. Можно встроить, куда захочется.

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

Идея и первые трудности

Я набросал спецификацию и решил, что рамка должна получать данные из Google Фото и отключаться, когда никого нет рядом.

Недолго думая, заказал Raspberry Pi 3 и дисплей с драйвером. Скачал спецификацию и нашел рамку с нужной глубиной.

В рамку должен был поместится Raspberry Pi и дисплейВ рамку должен был поместится Raspberry Pi и дисплей

Составил план действий тогда мне казалось, что осталось только написать код.

Мой план на тот момент выглядел так. Все круто: дисплей есть, Raspberry Pi работает, осталось написать кодМой план на тот момент выглядел так. Все круто: дисплей есть, Raspberry Pi работает, осталось написать код

Но не тут то было. Меня осенило: я купил не FullHD экран с разрешением 1920*1080, а WQXGA с чуть большим разрешением 2560*1600. А Raspberry Pi это же не полноценный компьютер. Если сравнить его с Core i7, то у последнего виртуальный коэффициент скорости 5199.98 BogoMIPS, а у Raspberry Pi всего 108.00. И тогда я понял, что он вообще, возможно, не способен показывать такие огромные картинки. К тому же по спецификации Raspberry Pi не поддерживает подобные дисплеи.

Но я быстро нашел решение этой проблемы вьюер feh, который отлично работает: выводит 2К картинки и даже слайд-шоу. Но, конечно, никакой анимации и плохая рандомизация: одну картинку можно было увидеть 5 раз. Но главное вывести картинки было можно. Это меня успокоило.

Выбор технологии и разочарование

Теперь мне оставалось только выбрать инструмент. Я выделил такие критерии отбора:

  • Есть на RaPi3

  • Легко делать визуальные эффекты

  • Можно делать UI

  • Быстро работает

Вот так выглядел мой шорт-лист:

  • C++

  • Phyton

  • Go

  • Java

  • Chrome + JS

Начал с С++. Вьюер feh лежит в опенсорсе. Почему бы просто не поменять его код? Я скачал, все было круто, но было одно НО: UI, эффекты и еще много всего нужно было делать вручную.

Все, с кем я обсуждал проблему, советовали взять Skia или OpenGL. Но я уже пытался писать трехмерные движки и знал, что совмещать OpenGL с Raspberry Pi это плохая идея. Raspberry Pi официально поддерживает только OpenGL|ES, и то с большими оговорками.

Тогда я решил пойти дальше.

Рассмотрел Go и Python. Go быстро работает и может компилироваться в native. Но меня удивило, что там нет UI-библиотек. Зато есть биндинги к Flutter Desktop: нужно писать на Go, а UI выводить через Flutter. От языка меня тогда немного мутило: он очень специфичный, хотя действительно простой и быстрый.

В Phyton много библиотек, но нет native. Это сразу меня остановило от его выбора, потому что Raspberry Pi совсем не шустрый. С UI тоже много вопросов: используй биндинги к Skia, и, возможно, будет тебе счастье.

Java же есть везде, попробую. Я нашел много библиотек. UI тоже можно делать легко, он идет из коробки. Со своими спецэффектами, но работает быстро. Java не умеет компилироваться в native на Raspberry Pi, но для работы это и не нужно.

Тогда я сделал прототип jSlideShow. Загрузка картинок работала. Я нашел библиотеку, которая позволяла делать разные эффекты. Даже умудрился прикрепить FPS-метр и выводить текст, если случались ошибки.

Когда я в первый раз запустил слайд-шоу на Raspberry Pi, то увидел черный экран. Две минуты смотрел на него, а потом прочел надпись, что картинка поменялась. Время между сменами картинок должно было составлять 10 секунд.

Оказалось, что Java очень специфично работает с картинками. Чтобы вывести на экран, Java конвертирует их в формат ARGB. Это приводит к резкому скачку памяти и дополнительным расходам на создание временных буферов. Тогда я и решил, что Java тоже не подходит.

Последняя надежда: Chrome + JS. Библиотек много, можно выбрать любую и быстро запрототипировать. Но ось грузится долго, Chrome тоже грузится долго. Создается ощущение, что пытаешься не вывести фотографии на фоторамку, а отправить сообщение в космос. Еще одно ограничение заключалось в том, что Chrome заточен под полноценный OpenGL, а на малине нет OpenGL и аппаратного ускорения в Chrome.

У меня совсем опустились руки, и я не понимал, что делать дальше.

Тогда я решил попробовать Dart и Flutter. Оказалось, что Dart есть в репозиториях Raspberry Pi. Примерно в то же время я начал пробовать Flutter, поэтому мне было легко на нем разрабатывать. По архитектуре это фактически биндинг Skia, все отлично.

Единственная проблема заключалась в том, что Flutter не подходит для embedded, потому что для Raspberry Pi нет скомпилированных исходников. Я начал искать в интернете, и оказалось, что один разработчик в ноябре 2018 года уже делал подобные эксперименты и описал все круги ада, которые ему пришлось пройти.

Я решил, что тоже справлюсь с этой задачей, и зашел в документацию Flutter Engine. Но из-за бурного развития языка она очень быстро устаревает. К тому же Google для сборки использует clang, из-за чего GCC поддерживается криво. А Raspberry Pi это закрытая система и toolchain только для GCC.

Тогда я попытался настроить все по инструкции. Потратил примерно 8 часов, но зря. Потом я попробовал обхитрить систему и весь Flutter Engine выгрузил в Raspberry Pi. Raspberry Pi это 2 гигабайта памяти и бесконечный своп на SD-карточку. Он пытался 12 часов что-то загрузить, но в итоге упал.

Потом на просторах интернета я нашел информацию про разработчика из Америки, который уже сделал Docker-контейнер, умеющий все собирать на ноутбуке. И за 10 дней с помощью фиксов мне удалось настроить и запустить это всё на домашней Ubuntu. Мне пришлось законтрибьютить в проект: я добавил Fullscreen и параллельно поломал TouchScreen.

В это время Google активно пилил Linux Support на Flutter, потому что все очень быстро менялось. И я получил очень много полезных знаний по этой Ninja build system.

В итоге я запустил галерею.

Так выглядела Flutter Gallery в 2019 году. Все работало очень медленно: около пяти кадров в секунду. И это я даже не говорю о 2К-картинкахТак выглядела Flutter Gallery в 2019 году. Все работало очень медленно: около пяти кадров в секунду. И это я даже не говорю о 2К-картинках

Когда я подключил свой дисплей, то выяснилось, что китайский драйвер очень капризный. По краям LCD-дисплея периодически появлялось мерцание. Еще одно ограничение: Raspberry Pi не поддерживает 2К-дисплей.

Я разочаровался и подумал, что проект уже не получится довести до конца. Плохое железо, плохой софт, плохая идея.

Выход Raspberry Pi 4 и новая надежда

В 4 квартале 2019-го года неожиданно вышел анонс Raspberry Pi 4 c поддержкой 4К-дисплеев и более мощным процессором. Тогда мои 2К точно должны были сработать. Я заказал его и через 2 недели получил новый Raspberry Pi с четырьмя гигабайтам памяти.

Тогда я все соединил.

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

Получилось даже побороть экран (о, как я тогда ошибался :) ). Оказалось, что в апреле 2020 года появился Flutter-pi проект, устраняющий все преграды, которые раньше мешали делать интерфейсы встраиваемых систем на Flutter. Его можно запустить через консоль, он быстро работает, поддерживает из коробки GPIO, Video, Touch и DRM (Direct Render Manager), позволяющий не инициализировать Х. Но единственный минус Flutter-pi запускается только в режиме Debug.

Удалось запустить и даже вывести изображения. Это уже мое приложение, которое работало на Raspberry PiУдалось запустить и даже вывести изображения. Это уже мое приложение, которое работало на Raspberry Pi

Я продумал архитектуру.

В моей идеальном мире архитектуру я представлял так: нативный слой, в котором используется GPIO; приложение на Dart, а внутри него Flutter isolate и Hardware isolate, который уже связывается с системой файлов; скрипты, отключающие экранВ моей идеальном мире архитектуру я представлял так: нативный слой, в котором используется GPIO; приложение на Dart, а внутри него Flutter isolate и Hardware isolate, который уже связывается с системой файлов; скрипты, отключающие экран

Борьба с оставшимися блокерами и слом идеальной архитектуры

А что насчет облаков? Оказалось, что Google предоставляет API драфтовый пакет. И в нем есть буквально все, кроме Google Photo. На официальном сайте компания пишет, что поддерживает REST API, Java и PHP и не использует автогенерацию, потому что API меняется. Тогда из Google Photo только собирались сделать социальную сеть.

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

Я подумал о том, что в Google же наверняка не пишут API вручную: они генерируются автоматически. А еще оказалось, что Photo API есть в пакете для Go. Да, я не очень люблю Go, но для написания прототипов он подходит идеально.

Я написал прототип, и все сработало: появилась возможность получать доступ к коллекции и качать оттуда фотографии. В Google Photo API есть JSON. Я поместил его в папку, из которой генерируется API. И получил обычный дартовый файл со всеми типизированными вещами, которые касаются Google Photo.

Следующим этапом нужно было подключить датчики. Я хотел, чтобы рамка выключалась самостоятельно и чтобы на рамке было три клавиши: меню, заморозить и выключить экран. Я сделал прототипы на Python, потому что это было достаточно просто: нужно вставить в текстовый файл 4 строчки кода, и все работает.

После этого я сделал маленькое приложение на Flutter, и оно тоже заработало. Это было очень легко, потому что Flutter-pi в примерах содержит работу с GPIO.

Я уже подумал, что никаких блокеров не осталось. Но выяснилась неожиданная вещь. Наверное, все знают о том, что во Flutter используется Native Channel, чтобы связываться с нативным API. Например, на Android Native Channel пишется для того, чтобы вызвать какую-нибудь штуку на стороне Android SDK. И разработчик Flutter-pi тоже использовал Native Channel, который обращается непосредственно в native-слой, и там уже идет работа с GPIO.

Во Flutter есть issue, и там есть целый тред со страданиями разработчиков из-за того, что Native Channel можно сделать только в UI isolate.

Так сломалась моя идеальная архитектура.

Из моего идеального мира пропал Hardware isolate, и вся нагрузка по работе с железом теперь идет только в UI-слое. А это очень плохо: когда UI-поток работает, экран пользователя не обновляетсяИз моего идеального мира пропал Hardware isolate, и вся нагрузка по работе с железом теперь идет только в UI-слое. А это очень плохо: когда UI-поток работает, экран пользователя не обновляется

Flutter считает, сколько времени занимает фрейм. Это усугубляет проблему: если фрейм занимает больше 16-ти миллисекунд, то Flutter начинает хитро пропускать кадры, а пользователь в это время получает отвратительный user experience.

Нужно было как-то решать эту проблему. Во Flutter-pi есть отдельный проект Flutter GPIO. Я нашел там открытый issue с призывом мигрировать на dart:ffi. Сказал себе: Challenge accepted, и потратил 20 ночей, включая 4 выходных дня, столкнулся со всеми возможными проблемами, но все-таки сделал библиотеку. У нее есть преимущества: возможность удаленной отладки (на Raspberry Pi), запуск из командной строки, синхронная и очень быстрая работа.

Но есть и ограничения: FFI не поддерживает Inline arrays. Это значит, что если вы в C++ хотите создать такой массив, то в Dart придется написать вот это:

Я сделал функцию в JavaScript, которая принтом вывела в консоль этот код. Я его вставил и не парился. Еще FFI не поддерживает bool: его можно представлять как int, ноль переключать в единицу. Но это не такая большая проблемаЯ сделал функцию в JavaScript, которая принтом вывела в консоль этот код. Я его вставил и не парился. Еще FFI не поддерживает bool: его можно представлять как int, ноль переключать в единицу. Но это не такая большая проблема

После того, как я поправил FFI, мой внутренний перфекционист успокоился. Все стало так, как я хотел. И тут мне написал мой коллега, Андрей Смирнов, предложил помощь с эффектами для смены картинок и нашел отличную либу с кучей эффектов. Андрей мигрировал её, чтобы она работала автоматически.

А еще Андрей сделал крутой виджет с информацией о состоянии устройстваА еще Андрей сделал крутой виджет с информацией о состоянии устройства

Проектирование корпуса фоторамки и сборка

На этом этапе у меня было все, что нужно: железо и софт.

Я купил маленький преобразователь до пяти вольт, потому что Raspberry Pi, драйверы, PIR и все остальное питалось от пяти вольтЯ купил маленький преобразователь до пяти вольт, потому что Raspberry Pi, драйверы, PIR и все остальное питалось от пяти вольт

Тогда я задумался о том, на чем проектировать корпус. Я хотел, чтобы спроектировать можно было быстро, а еще чтобы в программе была возможность параметризации. Я выбирал между Blender, Sketch и OpenSCAD, потому что у меня был небольшой опыт работы с ними.

Выбрал OpenSCAD опенсорсный САПР для параметрического создания объектов.

Я полностью сгенерировал печатные платы. В OpenSCAD выходные артефакты пишутся в виде кода, который чем-то похож на JavaScriptЯ полностью сгенерировал печатные платы. В OpenSCAD выходные артефакты пишутся в виде кода, который чем-то похож на JavaScript

Вот так выглядели бесконечные итерации:

Я ускорил весь процесс до 17 кадровЯ ускорил весь процесс до 17 кадров

На гифке кажется, что я что-то перемещаю. Но на самом деле я вырезал кусочки в OpenSCAD, печатал их и накладывал свои платы. Потом проверял, совпадают ли дырки: если есть отличие хотя бы на миллиметр, то все ложится криво. То же самое нужно было сделать для Raspberry Pi и проверить, входят ли все отверстия.

Печать занимает достаточно много времениПечать занимает достаточно много времени

Через 12 часов ожидания принтер заканчивает работу, и наступает самый ответственный момент сборка фоторамки.

А после я соединил все с дисплеем.

Вот так выглядит рамка в работе:

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

Финальная доработка

Мой внутренний перфекционист опять стал капать мне на мозг и говорить, что анимацию можно сделать получше: она немного подергивалась.

Я стал искать причину и выяснил, что их три:

  1. Виджеты на экране пересобираются много раз.

  2. Raspberry Pi замедляет CPU/GPU.

  3. Flutter debug mode.

Виджеты на экране пересобираются много раз. Я сделал на экране красную полоску, которая показывала таймер (я ведь тот еще дизайнер!). Эта полоска просто нереально нагружала процессор, потому что ему приходилось каждый раз перерисовывать виджеты. Я пофиксил это, и тогда анимация стала немного плавнее.

Raspberry Pi замедляет CPU/GPU. Оказалось, что когда нагрузки на процессор нет, Raspberry Pi по умолчанию начинает через несколько секунд снижать частоты. Фоторамка переключает картинки раз в 15 секунд, и за это время Raspberry Pi уходит в самые низкие частоты. Когда загрузка повышается, компьютер какое-то время ждет и только потом их поднимает. Это легко фиксится в конфиге /boot/config.txt.

Flutter debug mode. Осталось решить самую интересную проблему. Наверное, не секрет, что debag-режим это медленный режим для любой программы. Там очень много того, что не нужно на нативном устройстве. Я знал, что Flutter AOT должен работать быстро, но придется пройти все круги ада, чтобы собрать его для Raspberry Pi.

Необходимые шаги выглядели так:

Казалось бы, что может пойти не так? :)Казалось бы, что может пойти не так? :)

Чтобы собрать snapshot, необходимо сначала собрать Flutter Engine. Важно, чтобы его версия совпадала с версией локального Flutter. Они должны совпадать идеально, только тогда все получится. Из Flutter Engine можно получить frontend-сервер, в который помещается само приложение. После этого сервер выдает snapshot в виде бинарного файла. Потом этот snapshot нужно залить в snapshot-генератор и получить shared object.

Shared object это, грубо говоря, динамическая библиотека. Она содержит функции, которые можно импортировать.

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

В консоли есть команда, которая позволяет из shared object получать информацию. Нужно подключить shared object и при инициализации поместить его во Flutter Engine. Тогда получится не debug-режим, а нативное приложение.

Внезапно выяснилось неожиданное: библиотека не грузится по непонятной причине, и ни одна утилита в Linux не считает файл запускным: распознает ELF-файл, но не более того. dlOpen это метод в Linux, C++ и в C, который подключает шареный объект и по названию экспортирует методы. Именно он и не работал.

Readelf это же опенсорс, поэтому я пошел читать документацию по ELF-формату. Написал парсер ELF, получил ссылки на нужные области памяти и передал эту информацию во Flutter Engine.

И получил такой результат:

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

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

Я подумал, что дихотомия должна мне помочь. К тому моменту у меня был уже большой багаж знаний по SO и ELF. Я сравнил собранную библиотеку для Raspberry Pi libflutter-engine.so. с той библиотекой, которая не работала.

Оказалось, что есть небольшая разница:

Я заметил, что Linux по-разному оценивает запускаемый и не запускаемый файл. В рабочем варианте Linux переходил на определенную позицию в файле и считывал данные оттуда.

Оказалось, что по этому адресу хранится секция ARM, под которую собран бинарник.

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

Я решил из работающей библиотеки пересадить секцию в неработающую.

Что мне это дало:

  • Мгновенный старт.

  • Снижение потребления памяти с 275 Mb до 126 Mb.

  • Повышение скорости.

Но тут мне пришлось вспомнить правило: Работает не трожь!. Я обновил систему на Raspberry Pi. Вместе с обновлением установилась новая прошивка, и после этого отвалился дисплей. И теперь он иногда включается, а иногда нет. А еще он стал периодически моргать. Я пытался откатить обновление, но попытки пока что идут тяжело.

Итоги и планы на будущее

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

Я заказал новый драйвер от Toshiba для LCD, но он не помог решить проблему с экраном. Видимо, она кроется в самих дисплеях. Нужно подробнее разобраться, что именно перегревается и вызывает проблемы с подсветкой. Если у кого-то из вас есть желание и опыт в этом деле, напишите мне.

Еще я хочу сделать поддержку MQTT и веб-сервер для настройки с телефона.

Ссылки:

И напоследок полезный анонс для всех, кто интересуется разработкой на Flutter. 4-5 декабря мы организуем DartUP ежегодную конференцию по Dart и Flutter на русском и английском языках. В этот раз в онлайне и с крутыми спикерами из Google, Wrike, Яндекс, EPAM и не только. Если вам интересно узнать о последних новостях и кейсах с использованием этих технологий, в том числе в продакшне, регистрируйтесь до 4 декабря (участие в конференции бесплатное). Увидимся там!

Подробнее..

Перевод Релиз Dart 2.10 на шаг ближе к null-safety

09.10.2020 20:15:45 | Автор: admin

image<img src="http://personeltest.ru/aways/habrastorage.org/webt/bx/6z/zo/bx6zzobmc1fbzqkkzocurdk7gsg.jpeg" />


Команда языка Dart постепенно приближается к одному из самых мажорных релизов null-safety. Эта фича есть у многих лидеров рынка, включая Kotlin и TypeScript. По понятным причинам этот релиз нельзя сделать в виде рубильника: одним днём и простым апдейтом. Набравшее скорость сообщество, выпустившее огромное количество пакетов уже не сможет перейти на мажорную версию по щелчку пальцев. Поэтому процесс этот поступательный и не такой быстрый, как хотелось бы. Тем не менее сомневаться в том, что уже довольно скоро язык станет значительно лучше и удобнее не приходится!


Мы в Wrike не смогли обойти стороной обновление релиз Dart 2.10 и переводим статью из официального блога Dartlang.


Новый, унифицированный dart для всех ключевых задач. А также апдейт по таймлайну null-safety и принципах миграции.


Авторы: Майкл Томсен и Кевин Мур


Сегодня мы анонсируем новый Dart, версия 2.10 (two-dot-ten). В этом релизе представлен новый унифицированный Dart: единый инструмент для всех возможных задач разработчика, таких как создание проектов, анализ и форматирование кода, тестирование и компиляция приложений. У нас есть обновленная информация об этапах работ по null-safety, а также принципы миграции существующего кода.


Новый унифицированный инструмент разработчика Dart


Dart составляет основу Flutter SDK: он не только предоставляет язык и среду выполнения, которые используются в приложениях Flutter, но и поддерживает многие основные задачи разработчика, такие как форматирование, анализ и тестирование кода. Однако, в то время как Flutter всегда имел один универсальный инструмент разработчика (команда flutter), у Dart исторически было много небольших инструментов (например, dartfmt и dartanalyzer). Dart 2.10 обзавелся новым, унифицированным инструментом разработчика dart, очень похожим на инструмент flutter. Этот новый инструмент поддерживает все основные задачи, такие как создание новых проектов и пакетов, анализ и форматирование кода, а также запуск, компиляцию и тестирование проектов. Чтобы использовать этот инструмент, просто выполните команду dart:



Flutter включает новый Dart во Flutter SDK. Начиная с версии Flutter 1.22 SDK, директория <flutter-sdk>/bin (которая чаще всего находится в PATH) содержит команды и flutter, и dart. Если вы занимаетесь разработкой как на Flutter, так и на Dart, вы получаете возможности обоих из одного пакета Flutter SDK без необходимости устанавливать что-либо еще.


Если хотите загрузить и установить другой Dart SDK (если вам требуется другая версия), убедитесь, что SDK инструмента dart, который вы хотите использовать по умолчанию, находится в начале переменной окружения PATH.

В следующих стабильных релизах мы планируем расширить функциональность инструмента dart и постепенно отказаться от меньших инструментов (dartdoc, dartfmt, dartanalyzer и т.п.). В 2021 году мы планируем выпустить Dart SDK, содержащий исключительно инструмент dart. Рекомендуем вам переключиться на новый инструмент при запуске команд Dart сейчас, будь то вручную в терминале или в системах сборки (CI), и сообщить нам, если чего-то не хватает или что-то работает не так, как должно.


О null-safety


Мы хорошо поработали с null-safety с тех пор, как несколько месяцев назад запустили первую техническую превью. В полной null-safety мы видим инструмент предотвращения трудно обнаруживаемых null-ошибок, а также дополнительный бонус к улучшению производительности. Если хотите узнать больше, рекомендуем нашу новую страницу Understanding null safety. Если предпочитаете короткое видео, посмотрите the null safety video с мероприятия Flutter Day, прошедшего несколько месяцев назад.


Когда null-safety будет готова к использованию? Вот наш прогноз:


  1. Работа Flutter с техническим превью 2: Мы успешно перенесли большую часть Flutter. Ожидаем, что скоро вероятно, в течение следующего месяца среда Flutter будет полностью перенесена, и, таким образом, мы будем готовы к экспериментальному использованию Flutter. Вы сможете попробовать null-safety во Flutter и выполнить пробную миграцию ваших приложений и пакетов Flutter. Вам нужно будет использовать экспериментальный флаг. Не используйте экспериментальные функции в продакшене и не публикуйте перенесенные пакеты.


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


  3. Использование в продакшене stable: в зависимости от обратной связи на запуск бета-версии мы исправим все оставшиеся проблемы, а затем опубликуем их в stable. Трудно назвать конкретные сроки, но мы думаем о начале следующего года. Как только эта функция станет стабильной, мы надеемся на широкое внедрение null safety приложениями, опубликованными в сторах, и многими и пакетами, опубликованными в pub.dev в стабильных версиях.



Принципы перехода к null-safety


Мы хотели бы поделиться нашими главными принципами для миграции на null-safety.


Переходите, когда будете готовы


Null-safety это фундаментальное изменение в системе Dart. Это меняет основы объявления переменных, потому что мы решили сделать переменные ненулевыми по умолчанию:



Такое фундаментальное изменение было бы разрушительным, если бы мы настаивали на принудительном переходе. Мы хотим, чтобы вы сами выбрали подходящее время, поэтому null-safety это опциональная функция: вы можете использовать последние версии Dart и Flutter без необходимости включать null-safety. Вы даже можете иметь зависимость между пакетами с уже включенной null-safety и приложениями или пакетами, которые ее еще не подключили.


Переходите постепенно, по порядку


Мы настоятельно рекомендуем переносить код по порядку, начиная с листьев графа зависимостей. Например, если C зависит от B, который зависит от A, сначала перенесите в null-safety A, затем B, затем C. Этот порядок применяется независимо от того, являются ли A, B и C библиотеками, пакетами или приложениями.


Почему порядок так важен? Вы можете добиться некоторого успеха в переносе кода до миграции ваших зависимостей, но вы рискуете столкнуться с необходимостью повторной миграции, если ваши зависимости изменят свои API в процессе переноса. Мы предоставим инструменты, которые помогут вам узнать, какие из зависимостей были успешно перенесены. Если вы являетесь автором пакета, то, чтобы избежать риска нарушения API, подождите, пока все ваши зависимости не будут перенесены, прежде чем публиковать null-safe версию.


Используйте автоматизированные инструменты для снижения затрат на миграцию


Когда ваши зависимости готовы и вы решаете мигрировать, можете использовать наш инструмент миграции. Он работает, анализируя весь ваш существующий код, ищет, какие объявления могут быть не не допускающими null (они остаются без изменений), а какие должны допускать значение null (нужен маркер ? null в объявлении).


Инструмент миграции интерактивен, поэтому вы можете просмотреть свойства, которые он определил. Если вы не согласны с каким-либо из выводов инструмента, можно добавить подсказки о допустимости пустых значений, чтобы изменить вывод. Например, если вы хотите сделать API не допускающим null, даже если потребуется некоторый рефакторинг, можно сообщить об этом инструменту и повторно запустить анализ миграции. Добавление всего нескольких подсказок по миграции может оказать огромное влияние на качество перехода.



Получите полную выгоду при полном использовании


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


Дальнейшие действия


Новый инструмент разработчика dart доступен сегодня в SDK Dart 2.10 и в SDK Flutter 1.22. Если у вас уже есть Flutter SDK, вы можете получить Dart SDK с помощью команды dart, просто запустив flutter upgrade; это даст вам Flutter 1.22 SDK, в который встроен Dart 2.10. Рекомендуем вам немедленно переключиться на новый инструмент dart и связаться с нами, если что-то отсутствует или не работает должным образом.


Скоро у нас будет больше новостей о null-safety. Скорее всего, в течение следующего месяца, когда наши друзья в команде Flutter будут иметь фреймворк Flutter с поддержкой null-safety, готовый к экспериментам. Следите за обновлениями в блоге Flutter. А пока вы можете поэкспериментировать с null-safe кодом Dart с помощью DartPad с null-safety и узнать больше о дизайне функции в документации по null-safety.

Подробнее..

Перевод Обновление роадмапа AngularDart

23.10.2020 22:21:47 | Автор: admin


Недавно команда языка Dart опубликовала важный анонс про обновление роадмапа AngularDart. Мы решили перевести эту новость на русский язык и добавить свой комментарий о том, что это сообщение будет значить для Dart-сообщества.

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

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

Внедрение AngularDart внутри Google шло быстро, но внешняя веб-инфраструктура очень конкурентоспособна, и мы наблюдаем скромный рост популярности фреймворка среди команд, которые не входят в Google. В связи с этим мы переориентировались на проекты Google: новую Google Play Console и Google Ads. Мы хотим честно предупредить об этом, потому что стараемся быть прозрачными в отношении наших инвестиционных приоритетов для развития в ближайшие годы.

Одна из главных причин нашего решения сфокусировать AngularDart на Google apps стремительное развитие Flutter. Уже больше двух миллионов разработчиков используют его для создания красивых, быстрых, нативных приложений для мобильных, десктопов, а теперь и для веба. Разработчики просят дальнейшего развития, и мы намерены заняться этим.

Чтобы не было недопонимания: мы продолжаем развивать AngularDart. Но превращение успешного внутреннего проекта в успешный внешний проект это немалый шаг. Открытый исходный код продукта подразумевает больше, чем просто отправку кода в репозиторий GitHub и публикацию пакетов. Для успешного опыта пользователям нужны примеры и документация. Они ждут ответов на свои вопросы, исправления ошибок и развития фичей. И мы надеемся на бльшую вовлеченность пользовательского комьюнити в названных областях.

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

  1. Как можно скорее опубликовать долгосрочный стабильный релиз основных пакетов AngularDart в pub.dev. Они будут поддерживать последний стабильный Dart SDK и иметь обновленные зависимости.
  2. Продолжить обновлять исходный репозиторий последними внутренними изменениями. Сейчас они по большей части заключаются в обеспечении null-safety в базе кода.
  3. Сосредоточиться на обновлении пакетов до новой функции null-safety.


Если вы уже работаете с AngularDart, знайте, что мы будем продолжать поддерживать вас обновлениями. Но если вы только начинаете новый веб-проект на Dart, настоятельно рекомендуем посмотреть в сторону Flutter, который является нашим предложением для кросс-платформенной разработки в долгосрочной перспективе.
Для дополнительной информации о нашем продвижении по намеченному плану, смотрите исходный репозиторий и страницу пакета.

Примечание: это объявление касается только AngularDart. Angular Javascript Framework это совершенно отдельный проект.

Комментарий от команды разработки Wrike:

Многие слышали, что наша система (Wrike) является одним из самых крупных проектов в мире, написанных на Dart и AngularDart. Поэтому отношение к этому обновлению неоднозначное. Может прозвучать странно, но, несмотря ни на что, его можно назвать позитивным. Во-первых, про AngularDart не было слышно уже достаточно давно, и, хотя у нас есть прямые каналы связи с Google, мы ждали его не меньше других. Если сделать выжимку из сообщения, то вот что это будет значить для Dart-сообщества.

Появилась прозрачность в роадмапе AngularDart. Самая большая проблема веб-мира он очень конкурентен. Даже не так, ОЧЕНЬ конкурентен. Поэтому по-серьёзному выводить фреймворк на рынок, где уже есть такие гиганты как React, Vue или старший брат Angular (TS), может стоить очень дорого. И до этого момента была какая-то недосказанность: А что будет дальше?. Будет ли AngularDart пытаться завоевать мир, останется ли нишевой историей и вообще какой план? Часто новички, приходя в мир Dart, задавали вопрос: Я хочу веб приложение, что мне использовать?. Теперь ответ есть: Google делает ставку на Flutter. Учитывая его популярность (105k звёзд на GitHub!), это весьма разумный ход. Объём проделанной работы по добавлению документации, исправлению ошибок и качеству кода настолько велик, что AngularDart очень тяжело довести до такого уровня. Тем не менее, AngularDart остаётся открытым, и сообщество может вносить исправления и улучшения во фреймворк.

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

И самое позитивное: Flutter for Web привлекает к себе дополнительное внимание и ресурсы. Посудите сами: рынок мобильных устройств если не захвачен, то весьма неплохо накалён из-за Flutter. На рынке desktop-разработки вообще застой все используют браузеры (Electron, PWA). Рынка интерфейсов для IoT устройств вообще нет. Всё идёт к тому, что Flutter, набрав силу и скорость для входа в мир Web, сделает это в скором времени. Что из этого получится трудно загадывать, но мы надеемся, что у него всё получится.

На правах рекламы: четвёртый год подряд мы проводим DartUP, ежегодную конференцию посвященную Dart/Flutter и AngularDart. В этот раз конференция пройдёт в онлайн-формате. К сожалению, знаменитого крафтового Дарт-пива в этом году не будет. Но будет всё остальное: бесплатность, крутые спикеры, неформальное общение и неформальные форматы (да, вот так). Следите за новостями на dartup.ru и в телеграм-сообществе.
Подробнее..

DartUP 2020 в этом году онлайн и с известными спикерами

12.11.2020 12:19:49 | Автор: admin


4-5 декабря уже в четвертый раз Wrike при поддержке Google проведет DartUP единственную в России конференцию по Dart и Flutter. Современная реальность внесла коррективы в наши планы, поэтому в этом году DartUP пройдет в онлайн-формате. Это значит, что мы не будем обращать внимание на границы, трудности с логистикой и часовые пояса и позовём самых крутых спикеров, которые поделятся новостями об экосистеме Dart и практическим опытом.

Год назад в Санкт-Петербурге 420+ разработчиков слушали доклады про Dart и Flutter, соревновались в CodeBattle, выигрывали призы, неформально общались и пробовали фирменное Dart-пиво. 14 спикеров в двух залах поделились экспертизой и рассказали про зоны в Dart, работу с графикой на Flutter, Domain Driven Design и многом другом. Все доклады с прошлогодней конференции смотрите в плейлисте на Youtube.

Как это было в 2019:


В 2020 году DartUP станет лучше и масштабнее, а мы сохраним неформальную атмосферу нашего сообщества уже в онлайн-формате.

Снова будет два потока докладов: один на русском, другой на английском. Уже известны первые спикеры: Слава Егоров и Michael Thomsen из Google, Андрей Смирнов из Wrike, Евгений Сатуров из Surf, Александр Денисов из EPAM, Kevin Segaud, Геннадий Евстратов из Яндекс.Такси, Борис Горячев из Meduza и другие. Список будет пополняться, следите за анонсами. У всех участников конференции будет возможность пообщаться со спикерами в неформальной обстановке и задать вопросы после выступлений.

Помимо основного трека докладов, разработчики и дизайнеры из команды Wrike подготовили онлайн-активности турнир ботов-жуков, управляемых сгенерированным генокодом, и игру-битву Code Kombat, в которой можно победить противника, решив алгоритмические задачки на Dart и JS (посмотрим, чье кунг-фу круче).

По традиции, эксперты из Wrike ответят на вопросы про Dart и Flutter и поделятся опытом даже с новичками, делающими первые шаги в этих технологиях. Готовьте свои вопросы: на конференции будет возможность даже показать код для ревью или спросить совета, в том числе и у разработчиков языка из Google.



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

Следите за новостями: совсем скоро мы опубликуем программу конференции и подробно расскажем про спикеров и темы докладов. Да, в этом году DartUP пройдет без фирменного пива, но никто не запрещает подготовить своё. Мы от лица организаторов обещаем, что будет интересно. До встречи в декабре!
Подробнее..

DartUP 2020 архитектура Dart VM, non-nullability в действии и Flutter для бизнеса

26.11.2020 20:11:58 | Автор: admin


Уже 4 и 5 декабря пройдет DartUP конференция по Dart и Flutter на русском и английском языках. Обычно в это время мы смотрим площадку, печатаем стикеры и запасаем в офисе коробки со свежеприготовленным Dart-пивом. Но в этом году все будет по-другому. Под катом рассказываем про темы докладов, спикеров и онлайн-активности, которые нас ждут на DartUP 2020.

Программа


Слава Егоров разработчик Dart VM из Google, который уже 10 лет работает с Dart. Слава расскажет про архитектуру Dart Virtual Machine и ее эволюцию в ходе развития языка. Хардкорный доклад с огромным количеством примеров с кодом.

Michael Thomsen, Product Manager языка Dart из Google, проведет лайвкодинг-сессию на тему Dart non-nullability в действии. Недавно команда Dart выпустила null-safety один из важнейших релизов со времен второй версии. Во время своего выступления Майкл ответит на один из главных вопросов комьюнити: как переносить реальные проекты на мажорную версию.

Вместе с Filip Hracek, DevRel Flutter и Dart из Google, мы решили подготовить не обычный доклад, а веселый интерактив. Поэтому объявляем конкурс Cracking up Flutter: присылайте на wriketechclub@team.wrike.com свои Codepen с любым Flutter-приложением, которое не работает из-за ошибки в одной строчке кода, и правильный ответ. В теме письма укажите Cracking up Flutter.

Мы покажем ваши submissions Филиппу во время его выступления, и он за ограниченный промежуток времени попробует разобраться и найти место, в котором прячется ошибка. Присоединяйтесь к чату во время трансляции, чтобы помочь Филиппу в процессе! Участнику конкурса, который спрячет ошибку так виртуозно, что Филипп не сможет ее найти, мы подарим приз.




На круглом столе Flutter для бизнеса Борис Горячев (CTO в Meduza), Геннадий Евстратов (Head of iOS в Yandex.Taxi) и Александр Денисов (Co-Head of Flutter Competency в EPAM) поспорят о том, как продавать Flutter бизнесу и отвечать на три самых распространенных вопроса: А что если Google решит закрыть Flutter через год?, Где искать разработчиков? и Какие перспективы есть у Flutter?.

Kevin Segaud Dart и Flutter GDE, который уже выступал на DartUP в прошлом году. В этот раз Кевин расскажет про интересную и достаточно новую для комьюнити тему Dart FFI. Будет немного теории и много практики: Кевин в реальном времени покажет, как использовать Dart в связке с кодом C и расскажет про плюсы и минусы такого подхода.

Андрей Смирнов из Wrike знает о виджетах практически все. На прошлой конференции Андрей рассказывал про работу с графикой, а в этом году погрузится в устройство Flutter Engine, расскажет про Rendering Pipeline, Constraints и про то, как эти инструменты использовать на практике.

Кирилл Бубочкин из чешской компании Mews поделится опытом использования Flutter в продакшне: команда год назад переписала на Flutter свое большое B2B-приложение. На DartUP 2020 Кирилл расскажет про архитектурные подходы и полезные библиотеки.

Thomas Burkhart выступит с темой, которую редко удается встретить на Flutter-конференциях. Томас расскажет про RVMS практичную архитектуру для Flutter-приложений, поделится своим опытом и последними наработками.

Доклад Efthymis Sarbanis (Athens Flutter) круто зайдет в комбинации с предыдущим докладом Томаса. Efthymis Dart и Flutter GDE и организатор Flutter Greek Community. В своем докладе он расскажет про изоляцию фич во Flutter и использование принципов Domain-Driven Design и SOLID.

Взаимодействие с картами требуется во многих приложениях. Алиса Цветкова из HIQ разберет библиотеки карт для Flutter, расскажет о подводных камнях при работе с ними и о том, как построить взаимодействие вашего приложения с картами.

Леша Шаров из Wrike готовит доклад про нейронные сети на Dart. Во время выступления Леши мы поговорим про то, что из себя представляют простейшие нейронные сети и можно ли использовать Dart для их написания. А еще будет несколько работающих примеров.

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

Любителям хардкорных тем особенно понравится доклад Михаила Зотьева из Surf про внутренности Flutter: устройство Rendering, вывод виджетов и другие аспекты фреймворка. Будет полезно как новичкам, так и тем, кто хочет лучше разобраться во внутреннем устройстве Flutter.

Александр Денисов из EPAM расскажет про Navigator 2.0, который появился во Flutter относительно недавно. Саша расскажет, зачем они затащили его в проект, с какими сложностями столкнулись в процессе и что получилось в итоге.


Владимир Иванов из EPAM расскажет про проблему pixel perfect верстки, длинный фидбек луп на дизайн и про то, как инструмент Flutter Figma Preview может помочь в этой ситуации. Павел Мартынов из QuantumArt про особенности дизайна и разработки Flutter-приложений для AR-устройств. Андрей Скалкин из Datagrok поделится опытом создания высокопроизводительного веб-приложения на Dart.

Это далеко не полный список тем, о которых мы поговорим на конференции. Больше информации про спикеров, доклады и программу (которую мы опубликуем уже совсем скоро) ищите на dartup.ru.

Нетворкинг и онлайн-активности


Участники (и мы тоже!) любят DartUP не только за актуальные и полезные доклады, но и за неформальную атмосферу и возможность пообщаться с комьюнити.



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

Все неформальные нетворкинг- и Q&A-сессии будут проходить в SpatialChat. Там спикеры и эксперты из Wrike и Surf ответят на любые вопросы участников про Dart и Flutter. Готовьте свои трудные кейсы и приходите с кодом. Ребята из Surf объявили сбор идей и болей разработчиков для Open Source. А также эксперты из команды проведут код-ревью ваших репозиториев в прямом эфире. Все подробности по этой ссылке.

А пока присоединяйтесь к Slack-каналу, в котором мы будем постить анонсы во время конференции, отвечать на вопросы и неформально общаться.

Регистрируйтесь на DartUP до 4 декабря, готовьте вопросы спикерам и код на ревью. За день до конференции мы пришлем вам на почту ссылки на трансляции и активности. До встречи в декабре!
Подробнее..

Перевод Объявление о бета-тестировании null-safety Dart. Начало процесса миграции пакетов в надежное и безопасное состояние

27.11.2020 20:16:23 | Автор: admin


Безусловно, null-safety важный шаг в развитии языка. Команда Dart анонсировала бета-релиз версии с null-safety! Мы перевели на русский новость об этом релизе, в котором вы узнаете, как мигрировать на новые версии, какие преимущества получите, и в чем польза null-safety для всех нас.

Сегодня мы объявляем, что бета-версия с надежной null-safety доступна для Dart и Flutter. Null-safety это наше последнее важное достижение, призванное помочь вам избежать ошибок обращения к null-значениям класса ошибок, которые часто трудно обнаружить. Это видео в общих чертах объясняет причину нашей радости:


С переходом на бета-версию с null-safety пришло время для миграции тысяч пакетов, доступных на pub.dev. Мы перенесли библиотеки ядра Dart, фреймворк Flutter и более 40 пакетов Dart и Flutter. При этом мы надеемся, что комьюнити примет null-safety, мигрируя свои пакеты.



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

Выбор в пользу null-safety


Прежде чем обсуждать миграцию на null-safety, хотим повторить, что (как указано в наших принципах null-safety) у вас есть возможность выбирать, когда именно начинать переход. Приложения и пакеты будут работать только с null-safety, если их минимальное ограничение Dart SDK принадлежит по крайней мере к пред-релизной версии Dart 2.12:

environment: sdk: ">=2.12.0-0 <3.0.0"

Чтобы испытать ее, попробуйте создать небольшое null-safety приложение hello (например, с помощью dart create), содержащее код, как показано ниже. Затем можете попробовать запустить приложение до и после изменения ограничения SDK и запуска dart pub get и посмотреть, как меняется поведение программы. (Убедитесь, что dart --version возвращает вам именно 2.12)

bin/hello.dart:...void main() {  var hello = 'Hello Dart developers';  if (someCondition) {hello = null;  }  print(hello);} Before changing the SDK constraint:$ dart run null After changing the SDK constraint (and running dart pub get):$ dart run bin/hello.dart:6:13: Error: Null can't be assigned to a variable of type 'String' because 'String' is not nullable. hello = null;        ^


Переход к null-safety


Чтобы мигрировать пакет (или простое приложение) в режим null-safety, выполните следующие пять шагов, которые подробно описаны в руководстве по миграции на dart.dev.

Шаг 1: проверьте, готовы ли ваши зависимости


Настоятельно рекомендуем переносить код по порядку, начиная с листьев графа зависимостей. Например, если C зависит от B, который зависит от A, сначала мигрируйте на null-safety A, затем B, затем C. Этот порядок применим вне зависимости от того, являются A, B и C библиотеками, пакетами или приложениями.



Почему порядок так важен? Можно добиться некоторого прогресса в миграции кода до миграции зависимостей, но есть риск столкнуться с необходимостью повторного прогона, если ваши зависимости изменят свои интерфейсы во время миграции. Если некоторые из ваших зависимостей не null-safety, подумайте о том, чтобы связаться с издателями пакетов, используя контактные данные, указанные для каждого пакета на pub.dev.

Проверка готовности зависимостей


Чтобы проверить, готово ли ваше приложение или пакет к началу миграции, можете выполнить dart pub outdated в режиме null-safety. Приведенный ниже пример показывает, что приложение будет готово к миграции, если обновит свои зависимости на path, process и pedantic до пред-релизных версий, перечисленных в столбце Resolvable.



Если поддержка null-safety доступна в новых минорных версиях, вы увидите их в столбце Upgradable. Часто поддержка null-safety будет доступна в мажорных новых версиях; в этом случае вы увидите версии, перечисленные в разделе Resolvable в выводе утилиты outdated. Для перехода на них отредактируйте файл pubspec.yaml, чтобы разрешить эти мажорные версии. Например, можете поменять
process: ^3.0.13 на process: ^4.0.0-nullsafety.

Также вы можете найти пакеты с поддержкой null-safety на pub.dev, используя новые теги Null safety на страницах пакетов (например, collection 1.15) и новую опцию Расширенного поиска null safety search.



Шаг 2: перенос с помощью инструмента миграции


Если зависимости готовы, можете приступать к переносу вашего приложения или пакета с помощью инструмента миграции dart migrate.

Инструмент миграции интерактивен, поэтому вы можете просмотреть свойства null-safety, выведенные этим инструментом. Если вы не согласны с каким-либо результатом работы инструмента, можно добавить подсказки о допустимости пустых значений, чтобы изменить его. Добавление всего нескольких подсказок может оказать огромное влияние на качество миграции.



Несколько авторов пакетов Dart протестировали миграцию с использованием ранних предварительных сборок null-safety, и их отзывы были воодушевляющими. В руководстве по миграции есть дополнительные подсказки по использованию инструмента миграции.

Шаг 3: статический анализ перенесенного кода


Обновите пакеты с помощью pub get в вашей IDE или в командной строке. Затем используйте IDE или командную строку для выполнения статического анализа вашего кода Dart:

$ dart pub get$ dart analyze


Или на коде Flutter:

$ flutter pub get$ flutter analyze


Шаг 4: убедитесь, что тесты проходят


Запустите тесты и убедитесь, что они благополучно проходят. Вам может понадобиться обновить тесты, ожидающие null, если вы изменили код пакета на недопускающий значений null.

Шаг 5: опубликуйте свой null-safety пакет


Завершив миграцию и прогнав тесты, можете опубликовать свой пакет в качестве пререлиза. Вот краткое изложение наилучших практик:

  • Обновите версию до следующей мажорной версии (например, с 2.3.x до 3.0.0). Это гарантирует, что пользователи вашего пакета не обновятся до него, пока не будут готовы использовать null-safety. Это дает вам свободу рефакторинга API, чтобы наилучшим образом применить null-safety.
  • Переведите и опубликуйте свой пакет в качестве предварительной версии на pub.dev. (Например, используйте 3.0.0-nullsafety.0, а не 3.0.0.)


Для более подробной информации о миграции и управлении версиями см. руководство по миграции.

Преимущества гарантированной null-safety


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

Более безопасный код


Совсем недавно мы обнаружили ошибку в основной ветке Flutter, из-за которой различные команды инструмента flutter вылетали на определенных конфигурациях машины с ошибкой null: The method '>=' was called on null. Основной проблемой был недавний пулл-реквест на добавление поддержки для обнаружения Android Studio 4.1. Этот пулл-реквест добавил такой код:

final int major = version?.major;final int minor = version?.minor;if (globals.platform.isMacOS) {  /// plugin path of Android Studio changed after version 4.1.  if (major >= 4 && minor >= 1) {    ...


Можете найти ошибку? Поскольку версия может быть null, как основная, так и дополнительная версия также могут быть null. Этот баг может показаться легко находимым здесь в изоляции, но на практике подобный код проскальзывает постоянно, даже при строгом процессе код-ревью, который используется в репозитории Flutter. При null-safety статический анализ сразу же отлавливает эту проблему:



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

  • Внутренняя группа обнаружила, что они часто проверяют наличие null значений в коде, которые null-safety определяла как никогда не null. Эта проблема чаще всего встречается в коде, использующем protobuf, где необязательные поля возвращают значение по умолчанию, когда оно не задано, и никогда не имеют значения null. Это приводило к тому, что код неправильно проверял условие по умолчанию, смешивая значения по-умолчанию и null значения.
  • Команда Google Pay обнаружила баги в своем коде Flutter, из-за которых они не могли получить доступ к объектам Flutter State вне контекста Widget. До null-safety они возвращали null и маскировали ошибку; при null-safety анализ определил, что эти свойства никогда не могут быть null, и выдал ошибку анализа.
  • Команда Flutter обнаружила ошибку, из-за которой движок Flutter потенциально мог выйти из строя, если null был передан параметру scene в Window.render(). Во время миграции на null-safety они добавили подсказку, чтобы пометить Scene как не допускающую нулевое значение, а затем смогли легко предотвратить потенциальные сбои приложения, которые мог вызвать переданный null.


Использование надежной null-safety во время компиляции


Надежность null-safety Dart имеет еще одно важное значение: компиляторы Dart могут использовать информацию о допустимости значений null. Это может сделать ваши программы меньше и быстрее. Пока что у нас не так много реальных приложений, полностью переведенных на null-safety (ведь мы только сейчас начинаем миграцию экосистемы пакетов, от которых эти приложения зависят в плане надежности), но мы видим очень обнадеживающие результаты из основного фреймворка.

Недавно мы провели тестовую перекомпиляцию образца hello_world, чтобы измерить влияние null-safety на размер приложения. Это минимальный пример, который просто отображает hello world. При сравнении общего размера скомпилированного кода размер несжатого (установленного на устройстве) кода сократился на 3,5% без каких-либо действий кроме перекомпиляции с надежной null-safety. Это стало возможным несмотря на то, что все приложение состояло из 10 строк кода, потому что размер кода всех включенных библиотек сократился; например, сам фреймворк Flutter (package:flutter) сократился на 3,9%.

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

В некоторых случаях мы уже видели, как null-safety приводила к росту производительности, когда переход обнаруживал изъян в логике кода. Например, мы обнаружили проблему в кеше позиционирования текста во Flutter web. Этот кеш использовал ключ, допускающий значение null, а затем по заданной логике использовал TextAlign.start при null. Эта логика вызывала ошибку в кеше, когда элементы выглядели так, будто они изменились, даже если у них все еще было значение по умолчанию. В результате часто случались нерезультативные обращения в кеш. Добавление геттера textAlign, не допускающего значения null, помогло исправить ошибку кеширования, что привело к увеличению производительности рендеринга текста в 14 раз в случаях кешируемого текста.

Начните сегодня!


Бета-версии Dart и Flutter, содержащие null-safety, уже готовы. Если вы пишете на Flutter, можете переключиться на бета-версию с помощью flutter channel beta, а затем flutter upgrade. А если вы не используете Flutter, то можете получить автономный Dart SDK из архива Dart SDK.

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

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

Спасибо за вашу поддержку и обратную связь! Мы работаем над тем, чтобы сделать Dart более надежным языком, а Flutter более мощным фреймворком.

Michael Thomsen, продакт-менеджер Dart и Flutter, опубликовал эту статью в официальном блоге Dartlang. Если вы хотите послушать доклад Майкла и пообщаться с ним лично, приходите на DartUP 2020 Online 4 и 5 декабря и обсудите последние обновления языка с командой Dart и сообществом.
Подробнее..

Когда твой код стал общим история от дебюта до эндшпиля

11.12.2020 14:05:39 | Автор: admin


Отстаньте от меня, пожалуйста, я творец! Дайте мне творить!, программист Геннадий уже третий раз за вечер проговаривает эту мантру у себя в голове. Тем не менее пока что он не написал ни одной строчки кода, потому что в библиотеку, которую пытается развивать, прилетел еще один пулл-реквест. А, согласно политике компании, ревью кода должно проходить с минимальными задержками. Теперь Геннадий думает, как поступить: не глядя принять изменения, так же не глядя их отклонить или все-таки потратить драгоценное время, чтобы разобраться в их сути. Ведь кто, кроме него? Он этот код написал, он за ним и будет следить. А все изменения возможны только через его персональное согласие, ведь это Библиотека Судного Дня.

Тем временем буквально за стенкой команда с названием Кедровые бобры перераспределяет реквесты между собой, чтобы нагрузка по их просмотру ложилась более менее равномерно. Да, они не занимаются Библиотекой Судного Дня, но решают другие задачи, которые требуют быстрого изменения кода и более шустрых процессов.

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

Так какой путь мы проделали в Wrike?

Из каких вариантов мы выбирали свой способ владения кодом


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

Из очевидных минусов подхода это просто тоталитаризм в мире разработки. Геннадий без преувеличения единственный человек на Земле, который досконально знает код, имеет (или нет) планы на его развитие и может его изменять. Из-за поворота уже выехал тот самый автобус, который бас-фактор. Если Геннадий подхватит простуду, то, скорее всего, проект сляжет вместе с ним. Другим разработчиком придется делать форки, их станет очень много, и наступит полный хаос.

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

Условно персональный. Это как раз то, чем Геннадий заниматься не хочет: смотреть все МР, давать возможность изменять код своей библиотеки другим людям, но иметь полный контроль над изменениями и обладать правом вето. Плюсы и минусы такие же, как в прошлом пункте, но теперь они немного сглажены возможностью отправлять сторонним разработчикам пулл-реквест прямо в репозиторий, а не составлять ТЗ на реализацию каких-то фичей.

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

Из плюсов можно отметить большую скорость просмотра ревью, распределение экспертизы между участниками команды и снижение bus factor. Конечно, есть и недостатки. В обсуждениях в интернете многие упоминают отсутствие ответственности, если она размазана между несколькими людьми. Но это зависит от структуры команды и культуры разработчиков: за команду может отвечать Senior Developer или тимлид, тогда именно он будет входной точкой для вопросов. А МР и написание новых фичей можно разделять по уровню подготовки разработчика. Ведь было бы неправильным давать рефакторить код новичку, который только начинает разбираться в архитектуре приложения.

Как мы перешли к коллективному подходу владения кодом с человеческим лицом


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

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

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

Панель фильтров справа одинаковая для всех вьюшек. Это фича команды А. При этом все вьюшки это фичи других трех команд

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

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

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

Позжеперешли от конкретных людей к указанию команд. Правда, все в том же JSON-файле. Ощутимо лучше не стало, потому что теперь нужно было найти участников команды, которым можно отправить код на ревью. А у нас сотни (немного лукавлю, почти 70) фронтенд-разработчиков, и найти всех участников в то время было нелегко. Система владения уже стала коллективной, но искать нужных людей порой было не проще, чем искать заместителя владельца из прошлой версии. Плюс проблему с кодом, в котором могли пересекаться несколько фичей, все еще не удалось решить.

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

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

У Azure DevOps Services есть функциональность Automatically include code reviewer. Название говорит само за себя, и один мой бывший коллега говорит, что у них в компании этот инструмент используется и весьма успешно. Мы не работаем с Azure, так что было бы здорово услышать от читателей, как там дела с авторевьюером.

Мы пользуемся GitLab, поэтому было бы логичным шагом посмотреть в сторону GitLab Code Owners. Но принцип работы этого инструмента не подходил нам: функциональность GitLab это связка путей в репозитории (файлов и папок) и людей через их аккаунты в GitLab. Эта связка записывается в специальный файл codeowners.md. Нам нужна была связка пути и фичи. Причем фичи у нас содержатся в специальном словаре, где ставятся в соответствие команде. Это позволяет размечать сложные фичи, которые могут существовать не в одном репозитории, разрабатываться несколькими командами, и, опять же, не привязываться к конкретным именам. Плюс у нас были планы на использование этой информации для того, чтобы создать удобный каталог команд, связанных с ними фичами и всеми участниками команды.

В итоге мы решили создать свою систему контроля владения кодом. Реализация первой версии нашей системы была основана на возможностях Dart SDK, потому что сначала она была запущена для репозиториев фронтенд департамента и только для Dart-файлов. Мы использовали собственные мета-теги (благо это поддержано на уровне языка), затем статическим анализатором пробегали по всем исходным файлам и составляли что-то наподобие таблицы: Файл/Фича Команда-оунер. Размечать можно как отдельные файлы, так и целые пути с несколькими папками.

Спустя некоторое время разметка фичами у нас стала доступна для кода на Dart, JS и Java, а это вся кодовая база: и фронтенд, и бэкенд. Для того, чтобы получить информацию о владельцах, используется статический анализатор. Но, конечно, уже не тот, что был в первой версии и работал только с Dart-кодом. Например для Java-файлов используется библиотека javaparser. Эти анализаторы запускаются по расписанию и собирают всю актуальную информацию в одном реестре.

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

Кликнув по команде, вы сможете увидеть все фичи, всех участников команды, какие фичи сугубо технические, а какие продуктовые

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

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

А как вы решаете проблему владения кода у себя? И есть ли какие-то смежные с ним проблемы и, главное, их решение?
Подробнее..

Вжух, и прогоны автотестов оптимизированы. Intellij IDEA плагины на службе QA Automation

16.12.2020 12:18:50 | Автор: admin


Привет, Хабр. Я работаю QA Automation инженером в компании Wrike и хотел бы поговорить о том, как нам удалось оптимизировать процесс код-ревью для репозитория с 30 000+ автотестов при помощи IntelliJ IDEA плагина. Я расскажу о внутреннем устройстве плагина и о том, какие проблемы он решает в нашей компании. А еще в конце статьи будет ссылка на Github репозиторий с кодом плагина, с помощью которого вы сможете попробовать встроить плагин в ваши процессы.

Автотесты и деплой в Wrike


Мы пишем много разнообразного кода на Java в проекте автотестов. Сейчас у нас существует более тридцати тысяч тестов, которые тестируют наш продукт через Selenium, REST API, WebSocket и т.д. Все тесты разбиты на множество Maven-модулей в соответствии со структурой кода фронтенда. Проект активно меняется (релизы 1-3 раза в день), и, чтобы поддерживать качество кода, мы используем хорошо развитый механизм код-ревью. Во время ревью проверяем не только качество, но и работоспособность тестов.

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

Чтобы избежать таких ситуаций, мы решили добавить в процесс код-ревью одно действие. Автор merge request должен приложить ссылку на зеленый прогон автотестов в Teamcity, а ревьюер перейти по ней и проверить, что прогон действительно зеленый.

Какие проблемы могут возникнуть при запуске автотестов


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

У нас есть два механизма запуска тестов:

  1. По группам продуктовая разметка вида Epic/Feature/Story.
  2. По идентификаторам (id) любой автотест помечается уникальным числом, и можно запускать прогон по набору таких чисел.

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

Дальше события могут развиваться по нескольким сценариям.

Сценарий 1: Запущено меньше тестов, чем в действительности затронуто новым кодом.

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

Сценарий 2: Запущено больше тестов, чем в действительности затронуто новым кодом.

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

Чтобы решить эти проблемы, мы создали Find Affected Tests IntelliJ IDEA плагин, который быстро и надежно находит список id тестов, затронутых изменениями в коде проекта автотестов.

От бизнес-проблем к реализации


Но почему именно IntelliJ IDEA плагин?, спросите вы. Чтобы создать инструмент, который решит наши проблемы, мне пришлось ответить на два вопроса:

  • Откуда брать изменения кода?
  • Как по изменениям в коде найти затронутые id?

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

Ответом на второй вопрос стал PSI (Program Structure Interface). Я выбрал именно этот инструмент по нескольким причинам:

  1. Мы используем IntelliJ IDEA в качестве IDE.
  2. IntelliJ Platform SDK включает в себя PSI удобный инструмент для работы с кодом проекта, который используется и самой IntelliJ IDEA.
  3. Функциональность IntelliJ IDEA можно расширить за счет механизма плагинов.

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

Структура плагина


Давайте посмотрим на UI плагина, чтобы представить, что видит автоматизатор перед запуском поиска id:


Так выглядит упрощенная версия, которая выложена на GitHub

Все интуитивно понятно: запустили поиск, посмотрели результат. Результат состоит из двух частей: id автотестов и списка Maven-модулей, в которых эти автотесты находятся (зачем нужен этот список я расскажу дальше в этой статье).

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

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

Взаимодействие UI-части плагина с модулями схематично выглядит так:


AffectedCodeUnit и DisplayedData это классы для передачи данных между модулями

Git модуль


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

Для каждого файла собираются следующие данные:

  • Имя файла, имя модуля и путь до файла относительно корня проекта. Это позволяет в дальнейшем упростить поиск файла через механизмы PSI.
  • Номера измененных строк в файле. Это позволяет понять, что именно было изменено в структуре Java-кода.
  • Был ли файл удален или создан с нуля. Это помогает оптимизировать работу с ним в будущем и избежать ошибок в некоторых ситуациях (например, не обращаться к удаленному файлу через PSI).

Все эти сведения содержатся в выводе команды git diff. Пример результата выглядит так:


В выводе команды можно сразу заметить путь до измененного файла. Данные о номерах строк измененного кода содержаться в строках вида @@ -10 +10,2 @@. В этой статье я не буду подробно объяснять их смысл, но примеры можете посмотреть на Stackoverflow или поискать информацию про git diff unified format

Аргументами для git diff выступают текущая локальная ветка пользователя и remote master, а ряд ключей позволяет сократить вывод команды до нужного размера.

Каким образом происходит взаимодействие с Git в коде плагина? Я попытался найти встроенные механизмы IntelliJ IDEA, чтобы не изобретать велосипед. В итоге обнаружил, что для Git в IntelliJ IDEA существует свой плагин git4Idea. По сути это GUI для стандартных операций с проектом. Для доступа к Git в нашем плагине используются интерфейсы из кода git4Idea.

В итоге получилась такая схема:



Через класс GitBranch запрашивается diff, затем diff отправляется в DiffParser. На выходе остается список объектов класса AffectedCodeUnit (в них содержится информация об измененных файлах с кодом). О судьбе этого списка я расскажу в описании PSI модуля.

PSI модуль


Теперь информацию об измененных файлах нужно применить для поиска id автотестов. Чтобы разобраться, как по номерам измененных строк найти элементы Java-кода, нужно подробнее посмотреть на устройство PSI.

Как IntelliJ IDEA работает с произвольным проектом. Для PSI любой файл преобразуется в дерево отдельных элементов. Каждый элемент реализует интерфейс PsiElement и представляет собой структурную единицу кода для конкретного языка программирования. Это может быть целый класс или метод, а может быть закрывающая скобка небольшого блока кода.

Для нашей задачи элемент Java кода и есть PsiElement-объект. Здесь мы приходим к новой формулировке вопроса. Как по номерам измененных строк кода найти PsiElement-объекты? В интерфейсе PsiFile (представление файла с кодом в виде объекта) был унаследован метод findElementAt, который по сдвигу относительно начала текстового представления файла умел находить PsiElement.

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

Как выбрать нужный PsiElement-объект. Дерево, в которое IntelliJ IDEA отображает Java-код, может состоять из огромного числа узлов. Конкретный узел может описывать незначительный элемент кода. Для меня важны узлы конкретных типов: PsiComment, PsiMethod, PsiField и PsiAnnotation.

И вот почему:

String parsedText = parser.parse("Text to parse");

В этом фрагменте можно изменить разные части кода: имя переменной parsedText, входной параметр метода parse, код после оператора присваивания (вместо вызова parse можно вызвать другой метод) и т.д. Видов изменений много, но для логики работы плагина важно то, что фрагмент кода может находиться внутри какого-то метода, как в этом примере:

public String parseWithPrefix(Parser parser, String prefix) {    String parsedText = parser.parse("Text to parse");    return prefix + parsedText;}

В такой ситуации нет смысла пытаться понять, что поменялось в строчке кода с объявлением String parsedText. Нам важно, что изменился метод parseWithPrefix. Таким образом, нам не важна излишняя точность при поиске PsiElement-объектов для измененной строки кода. Поэтому я решил брать символ посередине строки как измененный и искать привязанный к нему PsiElement. Такая процедура позволила получить список затронутых PsiElement объектов и по ним искать id автотестов.

Конечно, можно придумать примеры Java-кода с диким форматированием, в котором нужно учитывать больше условий для поиска PsiElement-объекта. Как в этом примере:

public String parseWithPrefix(Parser parser, String prefix) {    String parsedText = parser.parse("Text to parse");    return prefix + parsedText; } private Parser customParser = new Parser();

Изменения в последней строке могут затрагивать как метод parseWithPrefix, так и поле customParser. Но у нас есть механизмы анализа кода, которые не допустят подобное в мастер.

Как устроен базовый алгоритм получения id автотестов по набору PsiElement-объектов. Для начала нужно уметь по PsiElement-объекту получать его использования (usages) в коде. Это можно сделать с помощью интерфейса PsiReference, который реализует связь между объявлением элемента кода и его использованием.

Теперь сформулируем краткое описание алгоритма:

  1. Получаем список PsiElement-объектов от Git модуля.
  2. Для каждого PsiElement-объекта ищем все его PsiReference объекты.
  3. Для каждого PsiReference объекта проверяем наличие id и сохраняем, если нашли.
  4. Для каждого PsiReference объекта ищем его PsiElement-объект и рекурсивно запускаем на нем описанную процедуру.

Процедуру следует повторять, пока находятся PsiReference-объекты.

Для задачи поиска абстрактной информации в коде алгоритм выглядит так:



В нашей задаче нужно найти id автотестов. Вот так можно применить блок-схему для этого:



Оптимизация алгоритма. В процессе реализации алгоритма я столкнулся с одной проблемой. Алгоритм работает с PsiTree-объектом, который состоит из большого числа узлов. Каждый узел соответствует элементам Java-кода. Механизм поиска PsiReference-объектов по умолчанию может находить довольно незначительные элементы Java-кода, из-за чего алгоритм будет совершать много лишних действий в процессе движения по дереву. Это замедляет работу алгоритма на несколько минут в тех случаях, когда найдено около тысячи тестов или больше.

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

public List<User> nonWrikeUsers(List<User> users) {    String wrikeName = "Wrike, Inc.";    return users.stream()                     .filter(user -> !wrikeName.equals(user.getCompanyName()))                     .collect(Collectors.toList());}

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

И тогда вместо четырех итераций алгоритма по каждому узлу дерева мы получим всего одну:



Проблему лишних операций в алгоритме удалось решить благодаря тому же ограничению типа данных PsiElement-объектов до PsiComment, PsiMethod, PsiField и PsiAnnotation. Именно эти PSI-сущности содержат всю релевантную информацию для поиска id затронутых автотестов.

Пример релевантной информации: затронуто поле какого-то класса (объект типа PsiField), оно могло использоваться в каком-то автотесте.

Пример нерелевантной информации: затронуто объявление локальной переменной (объект типа PsiLocalVariable). Здесь нам важно лишь то, где находится эта переменная (например, в теле другого метода). Само ее изменение нам ничего не дает.

В начало логики алгоритма я добавил поиск прародительского узла с нужным типом данных. Поиск осуществляется при помощи метода getParentOfType класса PsiTreeUtil. Для каждого типа PsiElement-объекта я создал реализацию логики обработки. Например, для прародителя типа PsiField отрабатывает объект класса FieldProcessing, а для PsiComment объект класса CommentProcessing. Код плагина исполняет подходящую логику в зависимости от результата работы метода getParentOfType.

В общем виде логика работы выглядит так:



Пример реализации логики обработки одного из типов PsiElement-объектов:



Подытожу основные основные особенности PSI модуля:

  1. Модуль работает по рекурсивному алгоритму поиска id автотестов. Краткая идея алгоритма заключается в поиске id для всех измененных PsiElement-объектов через механизм PsiReference.
  2. Алгоритм работает не со всеми узлами PsiTree-объекта, чтобы не совершать избыточные итерации.

Дополнительные возможности плагина


После того, как мы начали использовать плагин в работе, коллеги прислали несколько запросов по его улучшению. Это помогло усовершенствовать инструмент и оптимизировать прогоны автотестов.

Делаем работу с Git модулем гибче


В изначальной версии модуля можно было сравнивать текущую ветку с мастером (причем для ветки брался последний коммит). Но иногда это приводило к бесполезной работе инфраструктуры. А ведь именно с этим плагин и боролся.

Пример 1: автоматизатор создает merge request с одним коммитом. Он прогоняет 1000 тестов, которые затронуты этим коммитом. Ревьюер оставляет замечания по коду, автор merge request их исправляет. Эти правки затрагивают теперь только 200 тестов, но плагин предложит прогнать и 1000 тестов из изначального прогона, так как учтен первый коммит. Получаем лишние тесты для прогона.

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

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

Решение: я реализовал функциональность для сравнения локальной версии ветки с произвольной веткой (в общем виде с любым коммитом). Это позволяет гибко учитывать коммиты при работе с несколькими ветками.

Помогаем внедрить новую оптимизацию с помощью плагина


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

Тогда мы решили изменить сценарий запуска. Теперь пересобираются только модули, указанные вручную. Это позволило в среднем снизить время компиляции с 20 минут до 3-4.

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

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

Планы по развитию плагина


Плагин выходит в сеть. Мы автоматизировали сбор id затронутых тестов, но после автоматизатору все еще приходится выполнять набор однотипных действий для запуска прогона и дальнейшей работы с ним:

  1. Открыть нужный билд в Teamcity.
  2. Ввести данные для прогона.
  3. Сохранить ссылку на прогон для проверки его результатов.

Это отвлекает от решения других задач и приводит к возможным ошибкам: запуск в неверной сборке Teamcity, ошибки при копировании данных для прогона.

Так возникла задача: научить плагин запускать прогон в Teamcity самостоятельно. У нас есть специальный инструмент, который аккумулирует большинство данных, связанных с CI/CD процессами. Поэтому плагину проще отсылать запрос на запуск прогона этому инструменту (в нем же будет сохранена ссылка на прогон). Реализация этой логики позволит уменьшить количество рутинных действий после написания кода и сделает процесс запуска автотестов надежнее.

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

Идея состоит в том, чтобы несколько потоков одновременно занимались анализом разных узлов Psi-дерева. Для этого нужно грамотно выстроить работу с общими ресурсами (например, со множеством уже обработанных PsiElement-объектов). Одним из возможных решений может быть Java Fork/Join Framework, так как работа с деревом подразумевает рекурсивность. Но здесь есть подводные камни: у IntelliJ IDEA есть свой пул потоков, в который нужно грамотно встроить новую логику.

Несколько выводов


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

У нас есть сборка в Teamcity, в которой традиционно гоняются запущенные вручную автотесты. Она позволяет запустить прогон с указанием Maven-модулей. Я выбрал эту сборку для примера, потому что ссылку именно на нее автор merge request прикладывает во время код-ревью. А еще запуски в этой сборке выполняются вручную, и автоматизатор может взять набор id для запуска только из плагина (вряд ли кто-то в здравом уме будет собирать их руками).

Так выглядит график общего количества запусков в этой сборке:



В марте этого года (точка 03.2020) мы анонсировали сборку официально, а в апреле (точка 04.2020) я добавил в плагин вывод Maven-модулей, в которых находились затронутые автотесты. После этого количество запусков возросло в несколько раз. Следующий скачок произошел в сентябре, так как эта сборка стала использоваться для перезапуска автотестов в нашей внутренней деплой туле, но это нам не так интересно.

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



За последние полгода порядка 70-80% запусков автотестов происходят с указанием списка id: коллеги активно используют плагин по назначению. В апреле мы добавили в плагин вывод Maven-модулей. На графике видно, что это увеличило процент запусков с id с 50% до 75% за пару месяцев.

Действительно ли прогоны по id быстрее прогонов по продуктовой разметке? Статистика среднего времени прогона для данной сборки показывает, что да. Для id мы получаем примерно в три раза меньшее время, чем для продуктовой разметки: вместо условных 25-30 минут 8-10 минут.

Данные на графиках доказывают, что плагин активно используют. А это значит, что нам удалось увеличить количество прогонов в Teamcity, в которых запущенные автотесты подобраны по более надежному алгоритму, чем просто запуск по продуктовой разметке.

Пишите в комментариях про ваши успешные или не очень успешные примеры автоматизации.

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

Всем добра и побольше эффективных оптимизаций!
Подробнее..

DartUP 2020 итоги и видеозаписи докладов

23.12.2020 12:19:27 | Автор: admin


Вот и отгремел DartUP 2020! Два дня мы слушали доклады про Dart и Flutter, играли в Code Kombat и Bugz Arena и общались с участниками и спикерами в SpatialChat. Получилось ничуть не менее атмосферно, чем в офлайне. В этой статье мы подведем итоги конференции и поделимся с вами видеозаписями докладов на русском и английском.

Немного цифр про DartUP 2020:

  • 2 дня, 10 часов докладов и активностей
  • 29 спикеров
  • 2 200 зарегистрировавшихся на конференцию
  • 2 500 уникальных зрителей на Youtube

Топ-3 доклада на русском по оценкам участников


Вячеслав Егоров, Google 10 years of Dart

Слайды

Михаил Зотьев, Артём Зайцев, Surf Flutter под капотом

Слайды

Света Кривошеева, Effective Я не художник, я только программирую

Слайды

Топ-3 доклада на английском по оценкам участников


Efthymis Sarbanis Feature isolation in Flutter

Michael Thomsen, Google Dart non-nullablity in action

Rafa lsarz Slivers and Boxes How Scrolling Works

Слайды

Видеозаписи всех докладов со ссылками на презентации смотрите в плейлисте на Youtube.

Если захотите ненадолго вернуться на DartUP, посмотрите наш небольшой фотоотчет от иллюстратора Софьи Мироедовой. Так выглядела бы конференция, если бы проходила оффлайн.

Все материалы (иллюстрации, ссылки на видеозаписи и описания докладов) вы также всегда можете найти на официальном сайте DartUP.

Присоединяйтесь к русскоязычному сообществу Dart & Flutter в Telegram и Slack-каналу, в котором также можно продолжать общаться и задавать вопросы комьюнити.



Спасибо, что были с нами эти два дня! Надеемся на встречу в следующем году.
Подробнее..

Перевод Подготовка экосистем Dart и Flutter к переходу на null safety

26.02.2021 18:20:16 | Автор: admin

Поезд null safety мчится вперёд, уже почти официально анонсирован Flutter 2.0 (подключайтесь к предстоящему Flutter Engage), экосистема Dart тоже не стоит на месте. Мы перевели на русский язык новость из официального блога Dartlang и настоятельно рекомендуем вам переводить свои пакеты на новые рельсы, если вы этого ещё не сделали!

Вышел стабильный API для null safety

На днях вышла новая бета версия Dart, которая отличается повышенной стабильностью и наличием надежной null safety системы, над которой мы работали больше года. Обновленная бета (2.12.0259.9.beta) доступна на dart.dev и на бета канале Flutter. До выхода стабильной версии null-safe Dart критических изменений больше не предвидится.

Мы призываем разработчиков публиковать null-safe версии своих пакетов, чтобы пользователи смогли получить полноценную функциональность экосистемы на момент публикации стабильной версии null-safe Dart. Сами мы этот процесс уже запустили опубликовали стабильные версии null-safe пакетов, таких как args, yaml и grpc. Если все ваши зависимости в null-safe состоянии и опубликованы под стабильной версией (например, 1.0.0 вместо 1.0.0-nullsafety.123), вам пора заняться тем же!

На pub.dev мы также добавили новую фичу, которая сама размечает версии пакетов, помечая preview-релизы, если стабильная версия зависимого Dart SDK еще не вышла. Preview-релизы будут автоматически помечены как стабильные версии, как только состоится релиз стабильного Dart SDK.

Cтабильная (1.6.0) и preview (2.0.0) версии пакета args на pub.devCтабильная (1.6.0) и preview (2.0.0) версии пакета args на pub.dev

В руководстве по переходу на null safety есть вся последняя информация о том, как организовать миграцию ваших пакетов. Обратите особое внимание на ограничения Dart SDK и версии ваших зависимостей в pubspec. В том числе обратите внимание и на версию SDK, который вы используете для тестирования непрерывной интеграции (CI). Стабильная null-safe версия Dart выйдет уже скоро! Спасибо вам за поддержку!

Подробнее..

Повышаем качество кода с Dart Code Metrics

13.04.2021 14:10:30 | Автор: admin

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

Инструмент можно запускать из командной строки, подключать в виде плагина к Dart Analysis Server, а также в виде библиотеки. Запуск из командной строки позволяет легко интегрировать инструмент в процесс CI/CD, при этом результат анализа можно получить в одном из форматов: Сonsole, HTML, JSON, CodeClimate, GitHub. Подключение в виде плагина к Analysis Server позволяет получать оперативную обратную связь непосредственно в IDE.

Зачем мы решили создать такой инструмент? В Wrike уже написано примерно 2.5 миллиона строк кода на Dart. При таком размере кодовой базы невольно задумываешься о том, какова цена поддержки такого объема кода, как понять, что пора проводить рефакторинг, и с каких мест его стоит начать.

Вместе с Dart SDK нам поставляется Analyzer, у которого есть встроенный линтер с богатым набором правил. В официальном pub вы также можете встретить рекомендованные наборы правил: pedantic, effective_dart и т.д. Это помогает нам избежать глупых ошибок и поддерживать кодовую базу в той стилистике, которую предлагают авторы языка. Но мы столкнулись с тем, что нам не хватало аналитической информации по коду, а дальше завертелось.

Метрики

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

Сейчас анализатор собирает такие метрики:

  • Cyclomatic Complexity

  • Lines of Executable Code

  • Lines of Code

  • Number of Parameters

  • Number of Methods

  • Maximum Nesting

  • Weight of Class

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

Следующий шаг опираясь на данные нескольких метрик, инструмент находит антипаттерны в кодовой базе. На текущий момент реализовано всего два антипаттерна long-method и long-parameter-list. Мы думаем о том, чтобы расширить этот список.

Если с метриками типа Number of Parameters или Number of Methods достаточно легко разобраться, то как же считается, например, Cyclomatic Complexity?

Цикломатическая сложность части программного кода это количество линейно независимых маршрутов через программный код. Например, если исходный код не содержит никаких точек ветвления или циклов, то сложность равна единице, поскольку есть только единственный маршрут через код. Если код имеет единственный оператор if, содержащий простое условие, то существует два пути через код: первый если условие оператора if имеет значение true, второй если false. Подробнее про цикломатическую сложность можно почитать в статье на Википедии.

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

Для визуализации собранных метрик инструмент предоставляет различные форматы отчетов, подробнее о них я расскажу в части Отчеты.

Правила

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

Почему мы решили добавить линтинг в отдельный пакет, а не сделать PR во встроенный анализатор? У нас иначе спроектированы правила наличие расширенной конфигурации, возможность для каждого правила настроить строгость проверки. Например, error будет валить пайплайн, в то время как warning или style нет.

Текущий список правил выглядит так:

Общие

  • avoid-unused-parameters

  • binary-expression-operand-order

  • double-literal-format

  • member-ordering

  • member-ordering-extended

  • newline-before-return

  • no-boolean-literal-compare

  • no-empty-block

  • no-equal-arguments

  • no-equal-then-else

  • no-magic-number

  • no-object-declaration

  • prefer-conditional-expressions

  • prefer-trailing-comma

Специально для использования с библиотекой Intl

  • prefer-intl-name

  • provide-correct-intl-args

Специально под Dart Angular

  • avoid-preserve-whitespace-false

  • component-annotation-arguments-ordering

  • prefer-on-push-cd-strategy

Актуальный список вы всегда можете посмотреть в документации.

Речь идет не только про стилистические правила, но и про те, которые подсвечивают потенциальные ошибки: например, no-equal-then-else, no-equal-arguments и другие.

Часть наших правил основаны на проблемах, с которыми мы сталкиваемся на ревью и которые хотим покрывать автоматически, чтобы иметь возможность сосредотачиваться на самом главном. Другая часть появилась в процессе изучения списка правил таких инструментов, как PVS-Studio, TSLint, ESLint (большое спасибо им за вдохновение).

Рассмотрим подробнее некоторые из них.

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

Простой пример:

String method(String value) => ;

Здесь параметр value не используется, и для него анализатор выведет сообщение Parameter is unused.

Учитывая то, что в Dart можно наследоваться от любого класса и параметр может быть необходим в наследниках, в базовом классе он может быть переименован в _ . Тогда анализатор его пропустит.

Например:

String method(String _) => ;

Prefer trailing comma. Проверяет последнюю запятую для аргументов, параметров, перечислений и коллекций при условии, что они занимают несколько строк.

Например:

void function(String firstArgument, String secondArgument, String thirdArgument) {return;}

Для такой функции правило предложит добавить в конец запятую, чтобы после форматирования она превратилась в:

void function(String firstArgument,String secondArgument,String thirdArgument,) {return;}

Если параметры изначально помещались на одной строке, то анализатор не будет считать это ошибкой:

void function(String arg1, String arg2, String arg3) {return;}

Для правила также можно указать параметр break-on, который включает дополнительную проверку для указанного количества элементов. Например, без параметра break-on пример выше считается корректным. Но если сконфигурировать правило на break-on: 2, то анализатор покажет ошибку для этой функции и предложит добавить запятую.

No equal arguments. Проверяет передачу одного и того же аргумента более одного раза при создании инстанса класса или вызове метода/функции.

Предположим, что существует некоторый класс User и отдельная функция, создающая этого юзера:

class User {final String firstName;final String lastName;const User(this.firstName, this.lastName);}User createUser(String lastName) {String firstName = getFirstName();return User(firstName,firstName,);}

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

Member ordering extended. Проверяет порядок членов класса. Это правило получило постфикс extended: у нас уже было правило Member ordering, но оно было не настолько гибким.

Правило принимает конфигурацию порядка по достаточно гибкому шаблону. Он позволяет указывать не только тип члена класса (поле, метод, конструктор и др.), но и такие ключевые слова, как late, const, final или, например, признак nullable.

Конфигурация может быть задана, например, так:

- public-late-final-fields

- private-late-final-fields

- public-nullable-fields

- private-nullable-fields

- named-constructors

- factory-constructors

- getters

- setters

- public-static-methods

- private-static-methods

- protected-methods

- etc.

Или упрощена до:

- fields

- methods

- setters

- getters (или просто **getters-setters** если нет необходимости разделять)

- constructors

Подробнее про возможности правила можно почитать в документации.

Дополнительно правило может требовать сортировку по алфавиту. Для этого в его конфигурацию нужно передать `alphabetize: true`.

Отчеты

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

  • Console

  • HTML

  • JSON

  • CodeClimate

  • GitHub

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

Например, при выполнении команды:

$ dart pub run dart_code_metrics:metrics lib  # or for a Flutter package$ flutter pub run dart_code_metrics:metrics lib

При выполнении команды на кодовой базе Dart Code Metrics мы получили такой отчет в консоль:

Если захотим выбрать другой тип репортера (например, HTML), то нужно выполнить следующую команду:

$ dart pub run dart_code_metrics:metrics lib --reporter=html  # or for a Flutter package$ flutter pub run dart_code_metrics:metrics lib --reporter=html

Сгенерируется отчет в папке metrics. Результирующую папку также можно передать с помощью флага --output-directory или -o:

В отчете можно посмотреть каждый файл отдельно:

Чтобы посмотреть подробную информацию по метрикам, нужно навести курсор на значки слева от кода:

Если вы используете GitHub Workflows и хотите получить репорт сразу в созданных PR, необходимо добавить в пайплайн новый шаг:

jobs:your_job_name:...steps:...- name: Run Code Metricsrun: dart run bin/metrics.dart --reporter=github lib

Это позволит получить отчеты в таком формате:

Подробную информацию про все типы метрик и способы конфигурации можно посмотреть в документации.

Как подключить

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

Шаг 1: установите пакет как dev-зависимость.

$ dart pub add --dev dart_code_metrics# or for a Flutter package$ flutter pub add --dev dart_code_metrics

ИЛИ

Добавьте пакет вручную в pubspec.yaml.

Важно: если ваш пакет еще не переведен на null safety, используйте версию 2.4.1.

dev_dependencies:dart_code_metrics: ^3.0.0

Запустите команду установки зависимостей.

$ dart pub get# or for a Flutter package$ flutter pub get

Шаг 2: добавьте конфигурацию в analysis_options.yaml.

analyzer:  plugins:    - dart_code_metrics dart_code_metrics:  anti-patterns:    - long-method    - long-parameter-list  metrics:    cyclomatic-complexity: 20    lines-of-executable-code: 50    number-of-parameters: 4    maximum-nesting-level: 5  metrics-exclude:    - test/**  rules:    - newline-before-return    - no-boolean-literal-compare    - no-empty-block    - prefer-trailing-comma    - prefer-conditional-expressions    - no-equal-then-else

В этой конфигурации указаны антипаттерны long-method и long-parameter-list, метрики с их пороговыми значениями и правила. Весь доступный список метрик можно посмотреть здесь, а список правил здесь.

Шаг 3: перезагрузите IDE, чтобы анализатор смог обнаружить плагин.

Если вы хотите использовать пакет как CLI, то с документацией по использованию можно ознакомиться здесь.

Заключение

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

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

Подробнее..

Wrike уходит от использования языка Dart. Часть 1

16.04.2021 16:20:16 | Автор: admin

Данной статьёй мы хотим пролить свет на технический стек Wrike: каким он был раньше и каким мы видим его в будущем. Мы расскажем о том, почему пять лет назад мы выбрали язык Dart основным для frontend-разработки нашего продукта и почему сейчас мы решили посмотреть в сторону других языков и фреймворков.

Что такое Wrike?

Для полноценного понимания наших технических решений необходимо рассказать, что такое Wrike как продукт. Wrike это большая SaaS платформа для управления проектами и совместной работы команд. Когда мы говорим большая, имеется в виду не только количество возможностей самого продукта (о которых вы можете почитать здесь), но и кодовая база. За годы своего развития, пока продукт рос и эволюционировал, мы прошли большой путь от:

Wrike, каким он был в 2014Wrike, каким он был в 2014

До:

Wrike 2021Wrike 2021

Столь же стремительно эволюционировали технический стек и команда разработки.

Если постараться рассказать на пальцах, что такое Wrike, то стоит отметить, что в мире управления проектами есть довольно много must have фич, без которых трудно себе представить полноценный продукт на этом рынке:

Gantt Chart, календари, таблицы и это далеко не полный набор возможностей WrikeGantt Chart, календари, таблицы и это далеко не полный набор возможностей Wrike

Это хорошо иллюстрирует сложность и комплексность UI Wrike, что накладывает дополнительную ответственность на инженерную команду с точки зрения требований перформанса, скорости разработки и стоимости поддержки.

Краткая история технического стека

Wrike появился в 2006, но так далеко мы копать не будем. Историю frontend-разработки нового времени Райка можно условно поделить на несколько этапов, рассматривая последние шесть лет.

JS + EXT

На тот момент (2013-2014) мы уже написали достаточно внушительный объём кода на чистом JS, которому тогда не было альтернатив. В качестве основного движка (или фреймворка, если хотите) мы использовали Ext.js третьей версии. Несмотря на теперешнюю архаичность, вы будете удивлены, но он по-прежнему жив-здоров! На тот момент в нём было достаточно много прорывных возможностей, которые потом, через года, трансформировались в то, к чему мы привыкли сейчас. Например, data stores с некоторой натяжкой можно считать провозвестником привычных нам stores в React.

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

  • строгую типизацию

  • большие возможности из коробки

  • хорошую работу с большими объемами кода (сборка, минимизация и т.д.)

DART. Почему не TypeScript?

2014-2015 года были сложными с точки зрения принятия инженерных решений. Мы оказались перед выбором: использовать TypeScript, который тогда только-только вышел на стабильную версию или взять Dart, который был более зрелым, но менее распространенным. Подробнее вы можете прочесть тут.

Ключевыми моментами в нашем выборе стали:

  • Более строгая типизация. Как показало время, и Dart, и TypeScript двинулись в сторону более строгой системы типов. Dart полностью перешёл на sound систему типов, TypeScript по-прежнему имеет с этим некоторые сложности.

  • Возможности из коробки. Порой third-party libraries могут быть очень полезны, а порой вредны. Одна из проблем современного мира web, и ее TypeScript не решает, это обилие библиотек, которые могут помочь ускорить разработку, но которые при этом нужно выбрать, поддерживать и время от времени обновлять. Шутки про node_modules уже вошли в историю. Dart при этом имеет достаточно богатую встроенную библиотеку, core библиотеки обновляются и поддерживаются самим Google

  • Агрессивный Tree-Shaking. Так как Wrike имеет огромный набор фичей, которые в итоге превращаются в большой объём кода, язык должен был помогать нам не загружать большое количество кода на клиент (см. Minification is not enough, you need tree shaking by Seth Ladd, a также github).

Эти и некоторые другие особенности убедили нас сделать выбор в пользу Dart. И, оглядываясь назад на почти шестилетнюю историю Dart и Wrike, мы видим, что выбор был правильным. Конечно, мы прошли долгий путь от Dart 1.x с его динамической типизацией и интеграцией с Polymer до AngularDart и Dart 2.x. Dart помогал нам год от года растить продукт с инженерной и бизнесовой точки зрения, продвигая компанию и продукт в лидеры рынка Work Management Platforms (Gartner and Forrester ratings).

Текущее состояние

Сейчас мы написали на Dart уже 2.5 миллиона строк кода, а кодовая база состоит из 365 репозиториев. Мы создали большое количество решений для сборки и проверки Dart-кода: например, Dart Code Metrics. Без преувеличения отметим, что Wrike один из самых больших потребителей Dart за пределами Google, что касается его web-ипостаси (появление Flutter способствовало взрывному росту популярности Dart, но пока ещё по большей мере в мире мобильной разработки).

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

Экосистема Dart

Мы бы не хотели полностью пересказывать документацию, поэтому сосредоточимся на наиболее важной части фреймворках. Несмотря на то, что теоретически Dart позволяет работать со всеми web-фреймворками через JS interop, на самом деле выбор не очень большой:

  • OverReact обёртка над React от Workiva.

  • Flutter for Web популярный кроссплатформенный фреймворк, написанный на Dart, с недавнего времени поддержка web вышла в стабильной версии.

  • AngularDart де-факто стандарт для разработки web-приложений на Dart.

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

Главные причины нашего ухода от разработки на Dart

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

Вдобавок к этому существуют и модные течения даже в весьма хаотичном мире фронтенда. Какое-то время назад это был прогрессивный рендеринг (React Fiber, Angular Ivy). Сейчас появляется тенденция в виде отказа от глобальных state managers, для примера можно рассмотреть Effector. GraphQL, Server Side Rendering можно найти достаточно много вещей, которые обязательно должны быть поддержаны в современном веб-фреймворке.

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

И в этом фундаменте есть два составляющих элемента:

  • Код, который ваши инженеры пишут.

  • Код, который ваши инженеры НЕ пишут.

Современная разработка (особенно на фронтенде) щедро сдобрена использованием third-party библиотек и инструментов. Да что там, сейчас можно запустить продукт на рынок, вообще не написав ни строчки кода (так называемый no-code подход)! Тем не менее, код, который вы не написали это, с одной стороны, время, которое вы сэкономили, а с другой риск, который вы берёте на себя.

Разработка крупного продукта это всегда сложный баланс между написанием собственных решений / переиспользованием готовых / взаимодействием с разработчиками сторонних фреймворков. И используемые язык и фреймворк как одни из самых обширных и всепроникающих частей разработки становятся её наиболее уязвимым местом. В былые годы, когда продукты распространялись на дисках и концепция Continuous Delivery ещё не появилась, смена языка или фреймворка могла стоить критически дорого. Сейчас же, особенно с появлением концепции micro frontends, это не только не должно быть трагедией, а, наоборот, служит здоровым механизмом эволюционного развития.

Со всем вышесказанным приходится признать, что мы пришли к точке, где нам приходится пересмотреть свой текущий технический стек как не отвечающий нашим требованиям. Несмотря на то, что язык Dart и его экосистема движутся вперёд (в том числе благодаря взрывному росту популярности Flutter), а язык Dart становится всё лучше и лучше (например, с null safety) один ингредиент всё равно отсутствует web-фреймворк. Да, в самом языке уже есть примитивы, которые позволяют работать с DOM напрямую, но такая разработка может подойти для индивидуальных разработчиков, а не для больших команд.

Под отсутствием web-фреймворка мы имеем в виду, что никакое из существующих решений для языка Dart не обладает четырьмя необходимыми для современного web-фреймворка качествами:

  • Feature richness. Обеспечение работы со всеми (или большинством) возможностей, которые предоставляет современный web.

  • Performance.

  • Поддержка сообщества.

  • Развитие и добавление новых возможностей.

Если более пристально посмотреть на существующие фреймворки для языка Dart, то мы увидим, что:

AngularDart

Де-факто стандарт для веб-приложений. Отвечал почти всем требованиям, но, к сожалению, Google-команда сдвинула приоритет его развития в сторону Flutter. Это следует не только из твиттера Tim Sneath (менеджер Dart & Flutter):

Переписка о судьбе AngularDartПереписка о судьбе AngularDart

Но и из более официальных источников. Также можно прочесть ветку на GitHub. Да, AngularDart по-прежнему на месте, он жив, его можно использовать. Но ему не хватает одного из ключевых элементов: Развитие и добавление новых возможностей.

OverReact

Портированная версия React для Dart. К сожалению, поддержка комьюнити не очень большая, а сам проект разрабатывается в основном компанией Workiva.

Flutter for Web

Именно на этот фреймворк делают ставку инженеры Google. И он действительно движется вперёд взрывными темпами! Но то, что подойдёт небольшим компаниям или при портировании мобильного приложения на web, к сожалению, не подходит для наших задач.

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

Помимо этого, Flutter пока не имеет ряда немаловажных для современного web возможностей, например SSR или SEO.

Немаловажный аспект связан и со скоростью приложения: пока не совсем ясно, насколько Flutter сможет справиться, допустим, с табличным представлением. Мы проведём это исследование, когда появится возможность встраивать Flutter-приложение внутрь другого.

Таким образом, несмотря на нашу любовь к Dart и годы, которые мы прошли вместе, мы приняли решение двигаться в сторону изменения нашего технического стека, так как основная задача компании обеспечить возможность разработки приложения и через 2 года, и дальше, а с AngularDart мы объективно не можем этого гарантировать.

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

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

Подробнее..

Организация работы в креативной команде опыт Wrike, Miro, Revolut

18.09.2020 16:04:09 | Автор: admin


Мы в Wrike решили сделать встречу для сотрудников креативных команд дизайнеров, маркетологов, проджект-менеджеров чтобы поговорить об эффективных процессах там, где рутина может убить творчество. Позвали дизайн-лидов из Revolut, Miro и Wrike, чтобы они поделились своим опытом, наработками и секретами.
24 сентября, в 18:00 по Москве. Онлайн.


Спикеры:





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



В Miro мы делаем продукт для работы распределенных команд, и когда весь мир переключился на удаленную работу, мы столкнулись сразу с несколькими глобальными задачами:
Как трансформировать процессы в команде под удаленную работу
Как улучшить опыт + 4 миллионов новых пользователей
Какие ритуалы в дизайн-команде должны помогать обмениваться опытом и двигаться быстро

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



Приходите и готовьте вопросы спикерам. Мы всегда рады диалогу в это непростое онлайн-время.

Регистрация

Подробнее..

Митап Организация работы в креативной команде видеозаписи докладов

28.09.2020 14:11:21 | Автор: admin


24 сентября Wrike организовал митап для сотрудников креативных команд (дизайнеров, маркетологов) и проджект-менеджеров, чтобы обсудить, как построить процесс, который обеспечит прозрачность работы, предсказуемые результаты и разумные сроки выполнения даже самых глобальных проектов.
Дизайн-лиды из больших продуктовых компаний Wrike, Miro и Revolut на примерах показали, как эффективно решались их задачи благодаря правильной организации командной работы.
Если пропустили, делимся видеозаписями.


Катя Сюма, Miro Выход на ремоут: как всё не сломать и поддержать новых пользователей
В Miro мы делаем продукт для работы распределенных команд, и когда весь мир переключился на удаленную работу, мы столкнулись сразу с несколькими глобальными задачами:
Как трансформировать процессы в команде под удаленную работу
Как улучшить опыт + 4 миллионов новых пользователей
Какие ритуалы в дизайн-команде должны помогать обмениваться опытом и двигаться быстро



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


Дмитрий Щеглов, Revolut Revolut Figma Playbook: интерактивная организация проектов
Доклад о том, по каким правилам играет дизайн-команда, работая над проектами, какие дизайн принципы стоят во главе угла и какие критерии оценки качества мы используем. Как коллективно строить дизайн систему и как добиться того, чтобы проекты выглядели так, будто бы их делал один дизайнер, а не 30.
Подробнее..

Тысячи потраченных часов на компьютерные игры стоило ли это того

03.11.2020 18:06:31 | Автор: admin
В 1994 году, когда в США и Японии интерес к компьютерным играм в автоматах уже угас и на первый план вышли домашние игровые консоли, я все еще радовался, если мог запустить деревянную палку дальше своих дворовых друзей. А что поделать, Мурманская область образца 1994 года не могла похвастаться большим разнообразием гиковских развлечений. Зато, когда годом позже родители купили мне Dendy, количество тренировок по броскам палки резко сократилось. Кто знает, может быть, иначе я стал бы не программистом, а олимпийским чемпионом по метанию копья?



Одной из первых компьютерных игр считается Pong от Atari, которую выпустили в 1972 году. До 1995 набрали популярность и ушли в закат автоматы с Pac-Man, Missile Attack, появились шестнадцатибитные консоли и отрелизились Doom и Doom 2. Пока на западе полиция нравов решала, как запретить продавать компьютерные игры, а геймеры уже ассоциировались с тощими и бледными очкариками, на российском севере легко можно было стать лучшим другом любого подростка, если у тебя были Dendy или Sega.

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

Игры и хард скилы


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

Но эти поделки научили меня многому.

Английский язык. Чаще я выбирал в игре оригинальные тексты вместо фарша из автоматического переводчика.

Системы исчислений. Когда установленная игра не работала как надо, на помощь приходил Dial-up и руководства. Я разбирался, какие шестнадцатеричные или двоичные конфиги надо поправить, чтобы ее запустить.

Взломщики. Отдельное приключение, когда тебе нужно запустить два или три game.exe файла, предварительно расположив их в хитрой подпапке и удалив какой-нибудь conf файл. И это еще не было стопроцентной гарантией, что все заработает.

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

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

Игры и софт скилы


Важными для меня оказались и менее технические навыки.

Настойчивость и изобретательность. Многие механики в играх можно ненавязчиво комбинировать. Из последнего: в Zelda: Breath of the wild эти навыки пригодились в ситуациях с регулярным поджогом травы и длинными перелетами, заморозкой времени для дерева и полетом на нем. Еще одна игра Divinity: Original sin 2. Она основана на правилах D&D и позволяет творить все что угодно.

Командная работа. Этому учат мультиплеерные игры. В Battlefield 1942 я выбрал инженера не самый живучий класс в игре. Я пытался найти место применения основного навыка починки техники. И мне удалось: один игрок на танке занял позицию на холме и вполне успешно сдерживал осаду. А я всю игру чинил его танк. Танкист заработал больше сотни фрагов, а я как инженер около пяти.

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

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

Умение решать головоломки. Многие игры ими изобилуют: Zelda, Silent Hill 2, почти все квесты. Иногда приходится неплохо напрячься, чтобы их решить.

Желание писать. Это было действительно золотое время периодических журналов про игры: они выпускались с приложениями на дисках, демоверсиями игр, программами, кастомными картами.

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

Увидел на обложке Warcraft. Купил журнал. Купил в ларьке пиратскую копию Warcraft. Так и не поиграл, потому что взломщик не работает

Игры и жизненный опыт


Игры сказались на моем восприятии мира. Я старался поиграть во многих представителей жанра, не зацикливаясь на чем-то одном.

Хотя любимчики были.

Dendy и кислотные взломщики со своей восьмибитной музыкой подготовили меня к Crystal Castles лучше, чем чарты журнала Rolling Stones.

Стратегии научили планировать. Ресурсы, юниты, время все ограничено. Приходится достигать цели с тем, что есть, и не рассчитывать на внезапное улучшение положения.

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

Шутеры сформировали привычку всегда быть начеку, смотреть по сторонам и быстро реагировать на изменяющуюся обстановку. А еще искать укрытия.

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

Квесты и адвенчуры. Ох уж этот пиксель-хантинг! Теперь мне не кажется зазорным потратить несколько минут, если надо отмерить 137 пикселей мышкой.

Full Throttle. Красиво и брутально. Каюсь, не прошел до конца

Ужасы. Не. Ходить. Одному. Ночью. В. Пустом. Городе.

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

Хорошие игры могут познакомить с историей (Europa universalis), техникой (Ил-2: Штурмовик), культурой и мифологией (The Banner Saga). Конечно, они не претендуют на академические труды, но могут стать отправной точкой для более вдумчивого изучения.

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

Игры и сегодняшний я


Это все рефлексия о потраченных часах. Что же я имею сейчас? И не только я спросил у своих знакомых, как повлияло увлечение играми на них.

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

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

Поклонники Morrowind помнят, что у этого бедолаги можно добыть очень прыгучие свитки Икара. Главное, не разбиться самому. С другой стороны, редактор Morrowind позволял делать и не такие свитки

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

Есть ли у увлечения играми будущее для тех, кто только набирает свою четвертую или пятую тысячу часов? Мне трудно ответить на этот вопрос, но есть некоторые наблюдения.

  • Все ушло в онлайн, даже однопользовательские игры, а значит VPN, потоковое аудио и видео используются чаще, чем у меня (только по работе). Лелею надежду, что среди пользователей есть те, кто захочет понять, как же это все устроено.
  • Прошивки для консолей. Целые копии консолей в наше время уже невозможны, а прошивки вполне. Пускай современные геймеры не создают их сами, но некоторые могут применить по инструкции, если прижмет.
  • Да, любое злоупотребление приводит к болезням, уже сформировавшийся киберспорт это подтверждает: суставы, позвоночник, глаза все это довольно сильно изнашивается.

Жалею ли я о приблизительно 8000 часах, потраченных на игры? Однозначно нет. Хотя, может быть, немного жалею о том, что в то время у меня не было доступа к DnD. Но это уже совсем другая история.

А еще игры сформировали мой специальный словарь для важных переговоров. Он применим только в соответствующей аудитории, но его тоже считаю знаковым:

  • Нужно больше золота
  • BFG
  • Рокет-джамп
  • Астрологи объявили неделю <тут любое словосочетание по желанию>
  • Зерг раш
  • Потрачено
  • Our princess is in another castle!
  • Flawless victory!
  • Тебя даже вчерашний шторм не разбудил

Может быть, у вас есть свои?
Подробнее..

Callisto. Зачем мы придумали замену Selenium Grid

28.01.2021 16:20:38 | Автор: admin

На Хабре уже не раз писали о том, что у Selenium Grid есть проблемы, которые не решить простым способом (например: раз, два, три). В этой статье мы поделимся нашим опытом и расскажем, как нам в Wrike удалось построить стабильную инфраструктуру для Selenium-тестов.

TLDR: Мы написали своё open source решение и полностью заменили им Selenium Grid.

Мы уже рассказывали о том, как масштабировали свою Selenium-ферму с помощью Google Cloud Engine и Kubernetes. От очередей на запуск тестов мы избавились, но из QA-департамента регулярно поступали жалобы на нестабильность тестовой инфраструктуры.

Выбор пути

У нас не получилось заставить работать Selenium Grid в GKE так, чтобы нас это устраивало. В GKE мы использовали короткоживущие Preemptible VMs. Запуски тестов создают непродолжительную, но интенсивную нагрузку, и именно для таких случаев хорошо подходят эти короткоживущие инстансы. Их использование позволяет снизить расходы на инфраструктуру в несколько раз. Но, так как они могут быть выключены в любой момент, это увеличивает нестабильность Selenium Grid, который и сам по себе достаточно нестабилен. Так мы пришли к выводу, что надо искать ему замену.

Больше всего нам понравился Selenoid от Aerokube, в том числе потому, что в нём не используются компоненты Selenium Grid (Selenium Hub и Selenium Node). Но, к сожалению, он не работает в Kubernetes, а это было критично для нас: мы широко используем его в других частях нашей инфраструктуры.

В качестве альтернатив, работающих в Kubernetes, мы рассматривали Zalеnium, jsonwire-grid и Moon. Zalenium и jsonwire-grid используют в своей архитектуре Selenium Node, а мы очень хотели этого избежать. К тому же Zalenium прекратили разрабатывать.

Moon выглядел неплохо, но это платное решение с закрытым исходным кодом. У него богатая функциональность: например, в конфигурации Moon можно указать список поддерживаемых браузеров, организовав единую точку входа для всех тестов (такой подход используют в Яндексе). Нам это не требовалось, так как мы делали по-другому перед запуском тестов запускали Selenium Grid, настроенный на запуск определенной версии браузера, и после окончания тестов удаляли его.

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

Нужен ли свой велосипед?

Кодовая база Selenium Grid огромна. Но хорошая новость заключается в том, что писать такое же большое и сложное решение не нужно. Много кода в Selenium относится к работе с устаревшими версиями браузеров, а все популярные современные браузеры совместимы со стандартом W3C Webdriver.

В комплекте с каждым браузером идёт сервер, который предоставляет HTTP API (совместимый со стандартом W3C Webdriver) для управления этим браузером (дальше в тексте этот сервер будем называть просто веб-драйвером). Например, для Google Chrome это ChromeDriver, для Mozilla Firefox geckodriver и т. д. Поэтому при написании своего решения не требуется делать полноценную реализацию стандарта W3C она уже реализована в веб-драйвере.

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

С учетом этих условий остается только 2 основные задачи запуск/удаление браузеров и маршрутизация запросов до браузеров. Именно этим и занимается Callisto open source решение, которое мы разработали. Для запуска/удаления браузеров мы написали небольшое приложение, используя готовую библиотеку для работы с Kubernetes, а за маршрутизацию запросов отвечает Nginx. Получилось простое и надежное решение.

Архитектура Callisto

В архитектуре Callisto три основных компонента: Nginx, Callisto и podы с браузером и веб-драйвером.

Nginx делит все запросы на 2 группы:

  1. Запросы на создание/удаление сессии отправляет в Callisto.

  2. Все остальные запросы отправляет напрямую веб-драйверу.

Callisto при получении запроса на создание сессии:

  1. Создает pod с браузером и веб-драйвером.

  2. Перенаправляет запрос на создание сессии веб-драйверу и возвращает ответ.

При получении запроса на удаление сессии:

  1. Возвращает успешный ответ.

  2. Удаляет pod с браузером.

В качестве Docker-образов с браузерами используются образы от Selenoid.

Маршрутизация запросов до браузеров

Важным моментом является маршрутизация запросов от тестов к нужному podу с браузером. Нужно где-то хранить соответствие между session_id, которое вернул веб-драйвер, и конкретным podом. Первое, что приходит в голову это хранение информации на стороне Callisto. Мы немного изучили этот вопрос и смогли найти более простое и изящное решение, благодаря которому Callisto остался stateless-решением.

Как это работает: после того, как веб-драйвер создал сессию, Callisto модифицирует поле session_id в ответе веб-драйвера, добавляя имя и ip-адрес podа, и после этого возвращает ответ тестам. Соответствие session_id pod_ip хранится на стороне тестов.

Благодаря тому, что в стандарте W3C Webdriver session_id используется только в URL-ах, вторую часть обработки достаточно просто выполнить на стороне Nginx. Когда на Nginx приходит запрос, содержащий модифицированный session_id, он извлекает ip-адрес podа и оригинальный session_id и перенаправляет запрос этому podу с оригинальным значением session_id.

Примечание: имя podа Callisto использует при обработке запроса на удаление сессии.

Пример на картинке:

Дополнительные возможности Callisto

Web UI. В качестве Web UI используется Selenoid UI.

В Callisto поддерживается:

  • Отображение текущего статуса Selenium-сессий.

  • VNC.

  • Отображение логов веб-драйвера в реальном времени.

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

Примечание: поддерживаются не все функции Selenoid UI. Например, не работает ручное создание сессий и просмотр видеозаписей.

Мониторинг. Callisto экспортирует метрики в формате Prometheus: можно удобным образом наблюдать за состоянием Selenium-фермы.

Чистое окружение под каждый тест

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

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

Время создания podа с браузером сильно зависит от используемой инфраструктуры, но ориентироваться можно на ~10 секунд (может занимать до 2-х минут, если в Kubernetes-кластере включен автоскейлинг).

С какими проблемами мы столкнулись при разработке Callisto

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

499-е ошибки на Nginx. В логах Nginx периодически попадались 499-е ошибки. Они воспроизводились нестабильно: при максимальной нагрузке на Kubernetes-кластер (в середине рабочего дня) их было больше, а утром и вечером они практически не появлялись. При этом на стороне тестов ошибка проявлялась как Server disconnected error.

Nginx считал, что проблема на стороне клиента, а тесты считали, что проблема на стороне сервера.

Ошибка возникала либо на стороне Google Load Balancer, либо на стороне Ingress ControllerОшибка возникала либо на стороне Google Load Balancer, либо на стороне Ingress Controller

В результате исследований мы выяснили, что причина в некорректном значении параметра worker-shutdown-timeout. Его увеличение с 10 секунд (значение по-умолчанию) до 120 секунд помогло решить проблему.

Примечание: в ingress-nginx версии 0.26.0 значение по-умолчанию для этого параметра было увеличено с 10 до 240 секунд.

Медленное создание сервисов. При первоначальной реализации мы создавали Kubernetes-сервис для каждого podа с браузером, при этом имя сервиса соответствовало session_id.

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

При проведении нагрузочных тестов мы столкнулись с проблемой: создание сервисов начинает выполняться очень медленно при росте нагрузки на кластер (мы проверяли на GKE 1.12):

Так как мы хотели запускать 1000+ тестов параллельно в одном кластере, такое положение дел нас не устраивало. Мы пришли к решению с модификацией session_id, которое описано в разделе про маршрутизацию запросов до браузеров.

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

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

Медленное создание podов с браузерами. Нам удалось достичь приемлемого времени создания/удаления podов при одновременно бегущих ~500 тестах. Но нам требовалось обслуживать больше 1000 параллельных тестов.

Проблема решилась при обновлении GKE кластера с версии 1.12 до 1.14. В версии 1.14 время создания podов не превышало 10 секунд при более чем 2000 параллельно бегущих тестах.

504-е ошибки на Nginx. Некоторое количество тестов падало из-за того, что Nginx возвращал 504-ую ошибку. Проблема оказалась в том, что мы использовали Preemptible VMs. Когда Google забирал VM, все запросы к podам на этой VM падали по таймауту, что и приводило к данной ошибке.

В итоге мы настроили таймауты на стороне Nginx (чтобы такие запросы не висели долго) и добавили обработку этой ошибки на стороне тестов: при ее возникновении тест перезапускается.

Результаты внедрения Callisto

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

Кроме увеличения стабильности, оказалось, что при примерно том же количестве запускаемых тестов платить за GKE мы стали на 30-40% меньше. Такая экономия достигается за счет того, что Callisto более эффективно использует ресурсы кластера, запуская podы только тогда, когда от тестов приходят запросы. При использовании Selenium Grid pod'ы с браузерами часть времени находятся в запущенном состоянии, ожидая нагрузку. Также каждому pod'у с браузером нужно меньше ресурсов, т.к. не используется Selenium Node.

Красной линией отмечен переход с Selenium Grid на Callisto:

2019-20202019-2020

Репозитории:

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

Будем рады ответить на вопросы и комментарии про наше решение.

Подробнее..

Твиттер-конференция от Wrike киберпсихология на 280 символов

22.09.2020 22:05:57 | Автор: admin

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

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

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

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

Какая конференция и при чем тут твиттер?

Формат мероприятия очень простой. 29-го сентября в 17-00 начинаем твиттер-трансляцию в аккаунте @wriketechclub. Каждые полчаса один спикер рассказывает свой доклад через несколько твитов: 1 твит = 1 тезис доклада и слайд. Задавать вопросы, спорить и комментировать можно в треде даже после окончания твиттер-конференции: спикеры все равно никуда из Интернета не денутся :)

Обсуждать будем киберпсихологию взаимодействие с командами онлайн, власть, тимлидство, онлайн-активности во время пандемии и как вообще выжить в Интернете. Спикеров вы, разумеется, знаете:

За лучшие вопросы мы разыграем книги по софт-скиллам и отправим в любую точку мира.

Чтобы ничего не пропустить, подпишитесь на наш твиттер. Ждем вас 29-го сентября в 17-00!

Подробнее..

Круглый стол в Wrike Маркетинговая автоматизация инструменты, интеграции, процессы

01.02.2021 20:15:32 | Автор: admin

Откроем новый год событий в Wrike TechClub круглым столом по автоматизации маркетинговых процессов. 18 февраля в 18:00 (Мск) соберемся (виртуально, конечно) с одними из самых крутых экспертов отрасли, чтобы поговорить об актуальных вызовах для маркетинговых отделов.

Примерные темы для обсуждения:

Лиды.Маркетинговая воронка и воронка продаж

Автоматизация: процессы и инструменты

Lead nurturing

Кроссканальный маркетинг

Эксперименты

Спикеры:

Мариам Ванян, Head of Marketing Operations, Wrike

Ирина Манолова, Marketing Automation Specialist, JetBrains

Ксения Бородулина, Solution Engineer, CRM and Marketing Automation, Veeam

Анна Фомина, Email Marketing & Marketing Automation Team Lead @ Wrike

Надежда Николаева, Marketing Automation Specialist, Selectel

Сергей Лебедев, Marketo Administrator, Wrike

Встреча пройдет 18 февраля в 18:00 по Мск, онлайн.

Зарегистрироваться бесплатно

Подробнее..

Как я хотел разнообразить ретроспективы, а сделал собственную карточную игру

13.11.2020 12:14:13 | Автор: admin
Картинка с названием статьи

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

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

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

Я готов поспорить, что многие, читая эту статью, узнали себя. И я хочу обратиться к вам: Ребята, проблема не в вас, проблема не в Scrum, проблема в скучных людях. Никто не хочет обсуждать свои проблемы на встрече с другими людьми, которых также оторвали от привычной работы и которые тоже хотят быстрее вернуться к основным задачам.

Что мне не нравилось в ретроспективе и как я предложил это исправить


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

Только ретроспектива не давала мне покоя. На тот момент мы проводили ее по простым правилам:

  1. Каждый пишет на отдельных листочках плюсы и минусы прошедшего спринта.
  2. Каждый член команды комментирует написанное и вешает стикеры на доску.
  3. Все члены команды ставят галочки на листочках с важными для них плюсами и минусами. У каждого есть ограниченное количество голосов: от трех до пяти в зависимости от количества вывешенных листков.
  4. Все листочки сортируются от самых популярных до менее волнующих.
  5. В конце вся команда пытается придумать action items для каждого пункта сверху вниз. Ближе к концу уже запал заканчивается, но это не так страшно, так как самые важные пункты рассматриваются первыми.

В целом, такой формат встречи действительно давал плоды. Мы заводили action item для каждой проблемы, которая требует единовременного действия, и правило для исправления проблемы, которая требует регулярности (например, расширение definition of done для задач).

Но я видел недочеты, которые остальные члены команды либо не замечали, либо стеснялись проговорить:

  1. Очень редко кто-то предлагает обсудить глобальные проблемы всей команды. А в масштабах двухнедельного спринта их вообще может и не быть. Чаще мы говорили про локальные вопросы департаментов или чьи-то личные недовольства и радости. В таких случаях я терялся и с трудом выдавливал на коленке придуманную проблему, которая на самом деле меня и не волновала. Лучше уж так, чем совсем ничего не написать. Тогда остальные подумают, что мне безразлична жизнь команды, думал я.
  2. Подсвеченные плюсы обесцениваются. Каждый спринт мы писали о том, что вся команда или какой-то конкретный человек молодец. Это работает только только на нескольких начальных встречах. Спустя несколько месяцев такие похвалы уже не кажутся чем-то ценным.
  3. Члены команды, которые быстро пишут и голосуют, всегда находят время достать телефон, проверить почту, запушить коммит. Фокус и вовлеченность теряются. У всех складывается чувство, что они участвуют в чем-то не очень важном.

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

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

Спустя несколько спринтов пришла моя очередь. Я сразу был уверен, что хочу провести все в игровой форме. Также было понятно, что наш классический формат ретро дает хорошие результаты, но сам процесс имеет недостатки. Обдумав все это, я сел писать правила карточной игры под названием Retro King.

Основные правила игры


Весь игровой процесс делится на несколько этапов. Для сбалансированной игры количество карт в колоде должно совпадать с количеством игроков.

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

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

Макет карточки Strict Voter
После того, как все владельцы карт Problems Detector высказались, в игру выступают игроки с картами Strict Voter. Их задача проголосовать за наиболее важную проблему и объяснить свой выбор. Если количество голосующих четное и голоса разделились поровну, то остальная часть команды также получает право голоса.
Автор победившей в голосовании проблемы получает 2 балла.

Изначально непонятно, кому из игроков выпала карта Strict Voter и кто примет участие в голосовании. Поэтому владельцы карт Problems Detector должны рассказать про проблемы доходчиво и подробно, чтобы каждый член команды понял суть.

Макет карточки Positiveness
Вторая фаза игры. Игроки с карточками Positiveness называют по одному положительному аспекту своей работы за последние две недели. Последовательность и правила голосования такие же как в первой фазе, а победивший получает 1 балл.

Макет карточки Troubleshooter
Третья фаза игры. Игроки с карточками Troubleshooter предлагают свои решения проблемы, которая была выбрана на голосовании в первой фазе. Последовательность и правила голосования остаются такими же. Решение проблемы, набравшее наибольшее количество голосов, становится action item на следующий спринт, а его автор получает 3 балла. В конце карточки собираются и отдаются следующему за первым игроку. Круг смещается.

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

В качестве приза мы всей командой договорились переводить победителю bonus.ly это система благодарностей, которую мы используем внутри компании. Полученные бонусы можно обменять на мерч или отправить на благотворительность.

Какую пользу приносит этот формат по сравнению с классической ретроспективой:

  1. Если член команды не видит проблемы, которые хочет обсудить, то он может взять карточку с другой ролью.
  2. Проблемы должны быть сформулированы максимально понятно для каждого участника, потому что изначально неизвестно, кто будет голосовать.
  3. Голосование добавляет соревновательную составляющую. Каждый представляющий плюсы, минусы и решения хочет, чтобы проголосовали за его вариант.
  4. Похвала значительно приятнее, если ее кто-то озвучил в качестве плюса, а потом за это еще и проголосовали другие члены команды. Может оказаться, что новая вода для кулера наберет больше голосов, чем адресованная конкретному человеку похвала, но это уже проблемы озвучившего.

Производство карт для игры


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

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

Материал, на котором будут печататься карты.** Среднестатистический сервис по печати предоставляет достаточно широкий выбор: глянцевый картон, матовый картон, несколько видов сплендоргеля, перламутровая бумага, глянцевая бумага, крафтовая бумага и мелованная бумага.

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

Красочность (CMYK). Она обозначается набором цифр: 4+0, 1+1, 4+4. Цифры обозначают количество цветов CMYK, которые будут задействованы: 4 полноцветная печать, 1 черно-белая. В свою очередь цифра до нуля означает красочность лицевой стороны страницы, а цифра после обратную. Получается, что 1+0 это черно-белая односторонняя печать.

Размер карт. Я остановился на размере 70х100: такие карточки удобно лежат в руке и помещаются в карман.

Фотография распечатанных карточек

Результат


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

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

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

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

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

Ссылки для тех, кто захочет повторить мою игру:

Макет карточек

Генератор карт, который я использовал

Генератор паттернов для рубашки
Подробнее..

Как сделать статический сайт на Cloudflare Workers Sites

10.09.2020 14:18:39 | Автор: admin

Привет! Меня зовут Дима, я техлид SysOps-команды в Wrike. В этой статье я расскажу, как за 10 минут и 5 долларов в месяц сделать максимально близкий к пользователю сайт и автоматизировать его деплой. Статья почти не имеет отношения к тем проблемам, которые мы решаем внутри нашей команды. Это, скорее, мой личный опыт и впечатления от знакомства с новой для меня технологией. Я постарался описать шаги максимально подробно, чтобы инструкция оказалась полезной для людей с разным опытом. Надеюсь, вам понравится. Поехали!

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

Но вдруг вам все равно скучно и хочется прикоснуться к дивному новому миру технологий? Скажем, вы думаете об автоматизации развертывания и хотели бы по максимуму ускорить сайт? В этой статье мы будем использовать Hugo, но это не обязательно.

Для автоматизации мы используем Gitlab CI/CD, но что делать с ускорением? Давайте развернем сайт напрямую в Cloudflare с помощью Worker Sites.

Что требуется для старта:

Часть 1: Установка Hugo

Если у вас уже установлен Hugo или если вы предпочитаете другой генератор статических сайтов (или вообще не пользуетесь ими), то эту часть можно пропустить.

  1. Скачиваем Hugo с https://github.com/gohugoio/hugo/releases

  2. Помещаем исполняемый файл Hugo по одному из определенных в PATH путей

  3. Создаем новый сайт: hugo new site blog.example.com

  4. Меняем текущую директорию на только что созданную: cd blog.example.com

  5. Выбираем тему оформления (https://github.com/budparr/gohugo-theme-ananke/releases или что угодно)

  6. Создаем первый пост: hugo new posts/my-amazing-post.md

  7. Добавляем контент в созданный файл: content/posts/my-amazing-post.md.
    Когда все сделано, меняем значение draft на false

  8. Генерируем статические файлы: hugo -D

Теперь наш статический сайт находится внутри директории ./public и готов к первому ручному развертыванию.

Часть 2: Настраиваем Cloudflare

Теперь разберемся с первоначальной настройкой Cloudflare. Предположим, что домен для сайта у нас уже есть. В качестве примера возьмем blog.example.com.

Шаг 1: Создаем запись DNS

Сначала выбираем наш домен, а затем пункт меню DNS. Создаем A-запись blog и указываем для нее какой-нибудь фиктивный IP (это официальная рекомендация, но они могли бы сделать как-то посимпатичней).

Шаг 2: Токен Cloudflare

  1. My Profile -> API tokens tab-> Create Token -> Create Custom Token

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

Сохраните токен на будущее, он понадобится нам в третьей части.

Шаг 3: Получаем accountid и zoneid

Domain -> Overview -> [правая боковая панель]

Это мои, не используйте их, пожалуйста :-)Это мои, не используйте их, пожалуйста :-)

Сохраните их рядом с токеном, они тоже понадобятся нам в третьей части.

Шаг 4: Активация Workers

Domain-> Workers -> Manage Workers

Выбираем уникальное имя и тариф Workers - > Unlimited ($5 в месяц на сегодняшний день). При желании позже вы сможете перейти на бесплатную версию.

Часть 3: Первый деплой (ручное развертывание)

Я сделал первое развертывание вручную, чтобы выяснить, что же там происходит на самом деле. Хотя всё это можно сделать и проще:

  1. Устанавливаем wrangler: npm i @cloudflare/wrangler -g

  2. Переходим в директорию нашего блога: cd blog.example.com

  3. Запускаем wrangler: wrangler init site hugo-worker

  4. Создаем конфиг для wrangler (введите токен, когда его спросят): wrangler config

Теперь попробуем внести изменения в только что созданный файл wrangler.toml (здесь полный список возможных настроек):

  1. Указываем accountid и zoneid

  2. Меняем route на что-нибудь вроде *blog.example.com/*

  3. Указываем false для workersdev

  4. Меняем bucket на ./public (или где находится ваш статический сайт)

  5. Если у вас больше одного домена в пути, то вам следует исправить путь в рабочем скрипте: workers-site/index.js (см. функцию handleEvent)

Отлично, пора развертывать сайт с помощью команды wrangler publish.

Часть 4: Автоматизация деплоя

Эта инструкция составлена для Gitlab, но она передает суть и простоту автоматизированного развертывания в целом.

Шаг 1: Создаем и настраиваем наш проект

  1. Создаем новый проект GitLab и загружаем сайт: директория blog.example.com со всем содержимым должна находиться в корневом каталоге проекта

  2. Задаем переменную CFAPITOKEN здесь: Settings -> CI/CD -> Variables

Шаг 2: Создаем файл .gitlab-ci.yml и запускаем первое развертывание

Создаем файл .gitlab-ci.yml в корне со следующим содержимым:

stages:  - build  - deploybuild:  image: monachus/hugo  stage: build  variables:    GIT_SUBMODULE_STRATEGY: recursive  script:    - cd blog.example.com/    - hugo  artifacts:    paths:      - blog.example.com/public  only:    - master # this job will affect only the 'master' branch  tags:    - gitlab-org-docker #deploy:  image: timbru31/ruby-node:2.3  stage: deploy  script:    - wget https://github.com/cloudflare/wrangler/releases/download/v1.8.4/wrangler-v1.8.4-x86_64-unknown-linux-musl.tar.gz    - tar xvzf wrangler-v1.8.4-x86_64-unknown-linux-musl.tar.gz    - cd blog.example.com/    - ../dist/wrangler publish  artifacts:    paths:      - blog.example.com/public  only:    - master # this job will affect only the 'master' branch  tags:    - gitlab-org-docker #

Запускаем первое развертывание вручную (CI / CD -> Pipelines -> Run Pipeline) или отправляя commit в ветку master. Вуаля!

Заключение

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

Cloudflare WorkersHugoGitLab Ci

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru