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

Блог компании авито

OSCP как я сдавал самый известный экзамен по информационной безопасности

10.02.2021 10:11:22 | Автор: admin

Offensive Security Certified Professional отличается от других сертификаций по информационной безопасности адски сложным экзаменом. Ребята из Offensive Security бегают по интернету и пристально следят за тем, чтобы никакие подсказки или готовые решения не были доступны публично. И, похоже, им успешно удаётсяэто делать более 10 лет подряд.

Сдавая экзамен, люди не спят по двое суток, тестируя инфраструктуру на проникновение спостоянно включенной видеокамерой и спрашивают в специальном чате разрешения отойти втуалет. Я прошёл этот путь и сейчас расскажу, как всё устроено.

Я много читал про OSCP, но только волшебный пинок локдауна заставил меня купить курс. Заокном был 2020, а у меня $999 и желание прокачаться в информационной безопасности. Похоже, это отличное время, чтобы инвестировать деньги в обучение, подумал я и купил курс Penetration Testing with Kali Linux.

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

  • Книжку на 850 страниц.

  • 17+ часов видеолекций.

  • 30 дней доступа к лаборатории.

  • 1 попытку сдачи экзамена.

Сложности начинаются уже при покупке. Нельзя купить курс и сразу начать заниматься: можно только выбрать дату начала обучения в будущем. В моём случае датой стало начало октября 2020, через месяц после покупки.

Небольшая ремарка. Уже в процессе обучения я решил почитать блоги людей, которые проходили курс, и оказалось, что большая часть из них подошли к нему основательно и готовились за полгода или даже год. А для тренировки перед OSCP покупали доступы к HTB и VulnHub. Я же ничего из этого не делал, и курс был для меня чёрной коробкой.

После оплаты мне скинули ссылки на кастомизированный образ Kali (чем конкретно он отличается от обычного я не понял), а также архив для проверки VPN-соединения, который действует 3 дня. За эти дни нужно было скачать VMware, поставить туда этот Kali и проверить своё подключение к VPN. Говорят, раньше всё это присылали в день начала занятий, теперь присылают заранее.

Вообще в 2020 году курс изрядно обновили и, как они сами пишут, увеличили в два раза размер материалов раньше в книжке было всего 400 страниц, а также на 30% число машин в лаборатории. А ещё добавили новые типы атак, вроде атаки на Active Directory или лекций по PowerShell Empire, репозиторий которого уже закрыт владельцем.

Курс состоит из трёх частей

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

Вторая часть курса это время работы в лаборатории. Что это значит? Ты получаешь доступ взакрытый интранет, где располагается 4 виртуальных сети, связанных через шлюзы. На картинке из официального гайда можно посмотреть, как эти сети связаны между собой. Кроме того, в этой сети развёрнуто два полноценных Active Directory домена по 5 машин в каждом. Это, кстати, интересный момент, его я опишу ниже.

Изначально доступ есть только в Student Network (она же Public), но по мере захвата машин можно попасть и в другие сети. В сети 66 хостов, на каждом из которых лежит файл proof.txt, который необходимо сдать в панели управления. Принцип чем-то похож на CTF, с той лишь разницей, что сам файл не так важен как понимание уязвимости, через которую можно получить права администратора.

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

День Х

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

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

Я смотрел лекции по 2-3 часа в день и потому мне потребовалась целая неделя для просмотра всех 17 часов от начала и до конца, попутно листая PDF.

Лабы

После теории у меня осталось 23 дня, за которые я планировал получить доступ на все машины (ха-ха, как наивен я был тогда). Сначала в лабораторной сети у меня не было ничего, кроме адреса подсети /24, в которой расположены какие-то уязвимые хосты. Но именно так и построено обучение: в теории рассказали возможные векторы атак, а дальше всё зависит отстудента.

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

Подсказок, как ломать, нет в принципе. Можно пойти на закрытый форум для студентов, но это отдельный цирк. Обсуждать можно всё, но давать прямые подсказки запрещено. А потому местные посетители упражняются в аллегориях, например, вместо упоминания smb пишут brazilian dance, а вместо dirtycow используют well-know exploit.

На форумах ходит поверье о четырёх машинах (pain, sufferance, humble, gh0st), которые не обязательно ломать, так как они какие-то особенно сложные. Забавно, что узнал я об этом много позже получения доступа к каждой из них. По мне, так это обычные, ничем не примечательные машины, которые встречаются на любом CTF-соревновании.

Спустя 3 недели я закрыл всего 23 из 66 машин. В этот момент у меня заканчивался 30-дневный доступ в лабораторию, и я купил дополнительные 30 дней покупать дополнительные дни можно бесконечно. За следующие 30 дней я закрыл все машины, получив доступ ко всем подсетям.

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

Экзамен

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

Экзамен занимает полные 48часов, из которых первые 24 даётся на взлом 5серверов, а вторые 24часа на написание отчёта о том, как был получен доступ к каждому из них.

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

В день экзамена, за 15 минут до начала, на почту падает ссылка для проведения сессии подтверждения личности. Я использовал загранпаспорт. После это приходит уже новое письмо сдоступами для подключение к VPN и ссылка на экзаменационную панель управления.

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

  • 1 машина на написание эксплойта с переполнением буфера за 25 очков.

  • 1 комплексная машина со множеством ложных векторов атаки за 25 очков.

  • 2 обычные машины по 20 очков.

  • 1 совсем простая за 10.

Максимум можно получить 100 очков. Плюс ещё можно заработать 5 очков, конспектируя работу в лаборатории, но это вариант для параноиков. Лишние 5 очков точно не спасут, а выполнение и конспектирование всех заданий занимает очень много времени.

Так как экзамен это рулетка, то и соотношение Windows/Linux может быть любым. Мне попались три машины на Linux и две на Windows.

Первый полученный доступ стал на машине за 25 очков. Мне повезло: она была очень близка к стеку технологий, с которым я привык иметь дело. Затем, чтобы расслабиться, я получил доступ к машине за 10 очков, часто её называют low-hanging fruit. Для атаки требовалось просто найти один публичный эксплойт, который даёт доступ к учетной записи с правами администратора.

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

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

Я читал о смельчаках, которые не спали по 24 часа и всё равно проваливали экзамен. Примыкать к их числу я не хотел. Проснувшись за 3 часа до окончания экзамена, я проверил, хватает ли мне скриншотов и логов для заполнения отчёта. А после этого попытался ещё раз копнуть оставшуюся машину, но так ничего и не вышло.

Отчёт

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

Написание отчёта может отпугнуть неподготовленного русскоязычного обывателя. Но и у англоязычных студентов оно вызывало приступы паники. Некоторые счастливчики писали отчёты по 200+ страниц. Сказу скажу, что мой отчёт содержал всего 68 и это с учётом того, что я вставлял по одному скриншоту на лист.

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

После заполнения отчёта его нужно было запаковать в 7z и отправить на специальной странице. До 2020 надо было ещё и паролем шифровать, но теперь от этой практики отказались.

Результат

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

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

Подробнее..

Материалы Avito Design Talk видео и презентации

17.12.2020 18:19:07 | Автор: admin

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


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



Точки роста для дизайнера вкрупной компании Настя Ларкина, Авито


Работа дизайнера намного больше, чем красивые картинки. Настя рассказала, как участвовала взапуске чат-бота Авито Работы, икакие точки роста нашла для себя накаждом этапе создания продукта отформирования consumer journey map дообщения сразработчиками иредакторами.



00:00 Представление темы испикера
01:44 Что такое Авито Работа ивчём еёглавный продуктовый челлендж
08:09 Customer journey map Работы
09:13 Проблема продукта иеёрешение
13:16 Создание интерфейса чат-бота: исследование
17:22 Передача макетов вразработку
21:12 Проверка решения наканареечных запусках
26:49 Осознанная работа стекстом иеёвлияние нарезультат
31:00 Вывод: точки роста для дизайнеров


Посмотреть презентацию Насти

Значимость дизайнера наразных скоростях разработки Алексей Кандауров, Циан


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



00:00 Представление темы испикера
06:52 Определения: что такое значимость иразные скорости работы
13:13 Если нет ограничений посрокам, апостановка задач нечёткая
14:16 Край спектра нормальной продуктовой разработки
15:12 Другой край спектра нормальной продуктовой разработки
16:20 Метод быстрых экспериментов growth hacking
18:06 Дизайн кодом
22:20 Чему нас учит дизайн-сообщество
26:30 Мифы продуктового дизайна икак насамом деле
30:42 Парадокс значимости


Посмотреть презентацию Алексея

Дизайн вкраудсорсинге икраудсорсинг вдизайне Владимир Погорелов, Тинькофф


Владимир рассказал, как устроен внутренний краудсорс-проект Тинькофф под названием Klecks, поделился способами улучшенияUX, когда упроекта нетUI, иснижения порога входа вприложения для программистов.



00:00 Представление темы испикера
00:37 Что такое краудсорсинг иKlecks
02:58 Эволюция интерфейсов Klecks
14:20 Краудсорсинговые UX-исследования Тинькофф
21:23 Какие тесты можно проводить накрауде
22:38 Начто обращать внимание накрауд-тестах


Посмотреть презентацию Владимира

Круглый стол Личные проекты дизайнеров


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



На этом всё. Увидимся нановых митапах!

Подробнее..

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

09.10.2020 16:18:18 | Автор: admin

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



Текущее положение дел


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



Главная страница Авито



Страница поисковой выдачи



Страница объявления


Разработка этих страниц вызывала определённые проблемы:


  • Все шаблоны жили вогромной основной кодовой базе, где было сложно ориентироваться и вносить изменения.
  • После внесения даже маленьких изменений сборка проекта занимала продолжительное время до5-10 минут вхудшем случае и потребляла большое количество ресурсов компьютера.
  • Когда хотелось, чтобы отдельные компоненты работали наReact-е, приходилось дублировать вёрстку и вшаблоне, и вкомпоненте длятого, чтобы сделать механизм серверного рендеринга.

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


В связи сэтим появился npm-пакет, который мы гордо назвали single-page. Задача этого пакета содержать внутри себя верхнеуровневое описание каждой изстраниц. Это описание того, изкаких React-компонентов состоит страница, и вкаком порядке и месте они должны быть расположены. Также пакет призван управлять загрузкой нужных и выгрузкой уже ненужных компонентов, которые представляют собой небольшие React+Redux приложения, вынесенные вотдельные npm-пакеты.


Например, припереходе настраницу срезультатами поиска, пользователю необходимы поисковые фильтры, которые позволят сузить выдачу. Вданном случае single-page определяет, что необходимо запросить пакет сфильтрами и делает соответствующий запрос. Если пользователь уже перешёл настраницу объявления, то поисковые фильтры ему не нужны, а значит и сам пакет можно не использовать, чтобы он не ухудшал производительность страницы.


Сам пакет single-page уже работает как описано. Однако длятого, чтобы все описанные механики приносили пользу, переход между страницами должен быть бесшовным, как вSPA-приложениях. Ксожалению, намомент написания статьи бесшовных переходов между страницами нет. Но это не значит, что их не будет. То, каким образом сейчас работает пакет single-page это основа, которую мы будем развивать дальше. Она уже задала определённые правила, связанные сорганизацией кодовой базы, которых мы вюните придерживаемся.


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


Отдельные репозитории для npm-пакетов


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


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


Стало понятно, что необходимо переходить нановый стек React. Переход проходил поэтапно. Сначала вюните договорились, что все новые компоненты будут написаны сразу наReact-е и будут рендериться настраницу послееё загрузки. Эти компоненты слабо влияли наSEO, поэтому подход имел право нажизнь. Пример такого компонента блок спросмотренными и избранными объявлениями наглавной странице:



Для работы новых компонентов наReact-е иногда нужны были уже существующие компоненты, написанные наTwig-е. Поэтому мы начали переносить некоторые изних наReact вурезанном виде. Так, кпримеру, сниппет, который используется вблоке просмотренных объявлений, был частично перенесён нановый стек, хотя наTwig весь шаблон сразличными условиями, влияющими наинформацию всниппете, содержал более 1000строк.



Сниппет вблоке просмотренных объявлений, который был перенесён сразу нановый стек


А вот как выглядят сниппеты вразличных категориях:



Сниппет вкатегории бытовой электронике



Сниппет вкатегории работа



Сниппет вкатегории недвижимость



Сниппет вкатегории автомобили


Вынос вnpm-пакет это только часть работы. Длятого, чтобы пакет был полезен, он должен быть использован восновной кодовой базе. Поскольку все компоненты это отдельные пакеты, то вмонолите (основной кодовой базе) необходимо подключить их как зависимости, а далее проинициализировать эти пакеты вкоде. Так как блок просмотренных объявлений и сниппет были выделены вотдельные npm-пакеты, то вкодовой базе монолита они были подключены как зависимости. Чтобы увидеть изменения, которые появились сновой версией пакета, достаточно поднять его версию безвнесения каких-либо дополнительных изменений.


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


  • поднять его версию;
  • опубликовать её;
  • подключить новую версию вкомпоненте правого блока;
  • поднять его версию;
  • поднять версию вмонолите.

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


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


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


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


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


Что получилось наэтом этапе:


  1. Количество связанных пакетов выросло.
  2. Каждый пакет жил вотдельном репозитории.
  3. Вкаждый репозиторий создавался пул-реквест свнесением изменений, связанных споднятием версий зависимости.
  4. Ситуация, когда пакет не имел последнюю актуальную версию зависимости была нормой. Плюс, кэтому моменту уже создавался пакет single-page, который взависимостях имел все ранее созданные пакеты.

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


Переход к монорепозиторию


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


После небольшого эксперимента и ресёрча, стало понятно, что Lerna поможет решить часть наших проблем:


  • С ней не будет необходимости вкаждом пакете самим поднимать версии зависимых пакетов.
  • Отпадёт необходимость постоянно помнить прозависимости, потому что Lerna знает, какие пакеты какими версиями связаны.
  • Количество пул-реквестов уменьшится доодного, вкотором видны все зависимости, которые будут изменены.

Мы завели отдельный репозиторий, вкоторый начали переносить все пакеты и связывать их зависимостями черезLerna.


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


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


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

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


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


  • находил все зависимые пакеты;
  • поднимал вних dev или latest версию взависимости отпотребности;
  • добавлял changelog всем пакетам;
  • заменял новые версии вpackage.json файле монолита нановые и, вслучае dev пакетов, ещё и публиковал их.

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


Работа CI


Остаётся ещё один вопрос, который хочется подсветить: CI.


Так как репозиторий один, то и все тесты гоняются нанём сразу. Кроме того, появился внутренний инструмент, который позволяет писать компонентные тесты сиспользованием библиотеки Enzyme. Его написали ребята изплатформенной команды Авито и рассказали онём вдокладе Жесть дляJest. Это позволило покрыть сценарии, которые раньше нельзя было проверить. Так, например, появилась возможность проверить вызов попапа приклике на кнопку все характеристики впакете стехническими характеристиками дляавтомобилей.


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


Итоги и планы


Задва года мы прошли процесс перехода отбольшого количества слабо связанных репозиториев доавтоматизации работы смонорепозиторием черезLerna. Приэтом, есть гарантия того, что всё будет работать корректно благодаря прогону тестов накаждом пул-реквесте. Количество самих пул-реквестов уменьшилось с8-9 вхудшем случае додвух: вмонолит и вмонорепозиторий. Приэтом пул-реквест вмонорепозиторий содержит сразу все изменения.


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

Подробнее..

Оптимизация работы с PostgreSQL в Go от 50 до 5000 RPS

05.11.2020 20:23:57 | Автор: admin

Привет, меня зовут Иван, и я делаю Авито Доставку. Когда пользователь покупает товар сдоставкой, мы показываем ему список отделений служб доставки сценами. Цена доставки может меняться ототделения котделению. Мы смотрим наобласть карты, где покупатель ищет товар и информацию пообъявлению, например, координаты продавца, вес и размеры товара. И навыходе показываем человеку список отделений садресами и ценой доставки вкаждое изних.


Входе разработки калькулятора цены доставки возникла такая задача: есть структура базы данных PostgreSQL и запрос кней отсервиса наGo. Нужно заставить всё это работать достаточно быстро. Витоге нам удалось поднять пропускную способность сервиса с50 до5000RPS и выявить пару нюансов приобщении сервиса сбазой. Обэтом и пойдёт рассказ.



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


Структура базы данных


Структура базы данных


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


Объём данных такой:


  • send: ~400 тысяч записей;
  • receive: ~160 миллионов записей.

SQL-запрос


SELECT r.terminal_id,       r.lat, r.lon,       r.tariff_zone_id,       r.min_term, r.max_termFROM   receive rINNER JOIN (   SELECT DISTINCT ON (s.provider) provider, tariff_id, s.tag_from_id, Point(s.lat, s.lon) <-> Point (:seller_lat, :seller_lon) AS dist   FROM   send s   WHERE       s.lat BETWEEN :seller_leftbot_lat AND :seller_righttop_lat       AND s.lon BETWEEN :seller_leftbot_lon AND :seller_righttop_lon       AND :now BETWEEN s.active_from AND s.active_until       AND s.max_weight > :weight AND s.max_height > :height AND s.max_length > :length AND s.max_width > :width AND s.max_declared_cost > :declared_cost   ORDER  BY provider, dist   ) AS s USING (tag_from_id)WHERE   r.lat BETWEEN :buyer_leftbot_lat AND :buyer_righttop_lat   AND r.lon BETWEEN :buyer_leftbot_lon AND :buyer_righttop_lon   AND r.max_weight > :weight AND r.max_height > :height AND r.max_length > :length AND r.max_width > :width AND r.max_declared_cost > :declared_costLIMIT :limit;

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


CREATE INDEX send_idx ON send(lon, lat, active_from, active_until);CREATE INDEX receive_idx ON receive(tag_from_id, lon, lat);

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


Порядок полей виндексах тоже важен. Например, долготу(lon) есть смысл ставить виндексе впереди широты(lat): Россия вытянута вширотном направлении, и долгота оказывается более селективна.


Структура БД и SQL запрос.


Сервис наGo


Винтересах статьи сервис будет максимально упрощён. Он всего лишь:


  • разбирает входные данные;
  • формирует запрос и шлёт его вБД;
  • сериализует ответ базы вJSON и отдаёт его.

В реальности он ещё считает цену наоснове tariff_zone_id, но суть та же: основная нагрузка ложится набазу данных, вGo происходит минимум действий. Построен сервис наобычном Server изnet/http и использует одну горутину назапрос.


Архитектура решения


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


Для расчёта цены используем отдельный микросервис сосвоим хранилищем.


В качестве хранилища мы рассматривали Elasticsearch, MongoDB, Sphinx и PostgreSQL. По результатам исследования выбрали PostgreSQL: он закрывает наши потребности и приэтом существенно проще вподдержке длянашей компании, your mileage may vary.


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


Тестовый стенд



Сервис развёрнут вKubernetes натрёх подах по500Мб. База развёрнута вLXC-контейнере с4ядрами и 16Гб памяти. Вкачестве connection pooler используется pgbouncer, развёрнутый вконтейнере сбазой.


Это достаточно стандартный дляАвито сетап. Вреальном сервисе был бы ещё один pgbouncer, развёрнутый внутри каждого пода ссервисом.


План запроса


Посмотрим, как исполняется запрос кбазе данных:



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


Основные же затраты идут напоиск поbtree-индексу побольшой таблице.


Результаты в лоб


Пора уже запустить тест.



50RPS / 314мс для99-го перцентиля


Уже нанизких RPS появляются подозрительные пики вграфике времени отклика. Это видно насреднем графике, повертикальной шкале время вмиллисекундах. 70RPS сервис не держит совсем. Надо это оптимизировать.


Подход к оптимизации


Оптимизация это цикл изнескольких шагов:


  1. Определить цели и индикаторы. Чего хотим и как будем измерять успех.
  2. Создать тестовые данные. Внашем случае заполнить базу и сгенерировать ленту запросов ксервису.
  3. Добиться полной нагрузки одного изресурсов, увидеть узкое место.
  4. Расширить узкое место.
  5. Повторять додостижения целей.

Наши цели 200RPS минимум, лучше 500RPS. Индикаторы пропускная способность сервиса и время отклика.


Тестовые данные это важно


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


Изначально мы использовали ленту в20тысяч запросов. И довольно быстро удалось добиться такого результата:



Тут сервис держит 1000RPS при52.5мс. Всё красиво, кроме скачков времени отклика, но давайте попробуем потестировать ту же конфигурацию наленте в150тысяч запросов:



Уже на200RPS сервис заваливается. Запросы отваливаются потаймауту, появляются 500-ки. Оказывается, предыдущий тест врёт примерно в10раз попропускной способности.


Похоже, дляленты в20тысяч запросов использовалась лишь часть данных втаблицах. PostgreSQL смог всё закэшировать и работал быстро. Дляленты в150тысяч требовалось больше данных изтаблиц, в кэш они не поместились, и быстродействие упало.


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


Переезд на pgx/v4


Скачки времени отклика награфиках выше намекают наналичие проблем вподключении сервиса кбазе.


Досих пор мы использовали библиотеку pgx третьей версии и не ограничивали размер пула соединений. Если уж оптимизировать эту часть, то давайте переедем начетвёртую версию и уже наней будем всё настраивать. Тем более, пронеё есть много хороших отзывов.
Переехали, ограничили пул до10соединений. Пробуем:



Стало лучше, но принципиально ничего не изменилось. Вчём дело? Смотрим метрики pgbouncerа:



Синий число активных клиентских соединений (cl_active), красные точки число клиентских соединений, которым не досталось серверного соединения (cl_waiting, правая шкала, снимается раз в30секунд).


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


Откат на pgx/v3


На тот момент баг вpgx/v4 еще не был исправлен, и мы воспользовались воркэраундом: откатились натретью версию и отключили отмены запросов.



Сильно лучше не стало, но самые хорошие новости ждали нас вметриках pgbouncer:



Число активных соединений растёт домаксимума поднагрузкой здесь пул ограничен до12 и не падает доконца теста.


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



130RPS при20параллельных запросах


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



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


Масштабируем базу по диску


Проверим, является ли узким местом диск. Увеличиваем квоту контейнера в8раз, смотрим:



Открытый тест: 500RPS / 109мс



Закрытый тест: 745RPS


В закрытом тесте пропускная способность выросла со130RPS до745 почти линейный рост. Значит, мы действительно упираемся вдиск.


Оценим предел вертикального масштабирования. Снимаем сконтейнера ограничение наоперации ввода-вывода вообще:



Открытый тест: 3000RPS / 602мс



Закрытый тест (20инстансов): 2980RPS / 62мс


Заметим, что закрытому тесту явно не хватает 20параллельных запросов, чтобы нагрузить сервис. Мы съели вообще весь диск навсём сервере сбазой:



Зелёное число операций чтения (растёт вниз)


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


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


Масштабируем базу по памяти


Смотрим размеры наших таблиц и индексов:


SELECT      pg_size_pretty( pg_total_relation_size('send')) send,      pg_size_pretty(pg_indexes_size('send')) send_indexes,      pg_size_pretty( pg_total_relation_size('receive')) receive,      pg_size_pretty(pg_indexes_size('receive')) receive_indexes;

Видим 21Гб данных и 6Гб индексов. Это существенно больше полезного объёма данных, но тут Постгресу виднее.


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


Смотрим список конфигураций и находим вот такую:



8ядер, 64Гб памяти, effective_cache_size 48Гб


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



Открытый тест: 4000RPS / 165мс



Закрытый тест (100инстансов): 5440RPS / 106мс



Операции чтения (зелёное) нануле, операции записи (жёлтое) внезначительных количествах


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



Утилизация CPU полностью загружены все 8ядер


Теперь мы упираемся впроцессор. Это хорошо: масштабировать его относительно просто, а мешать мы никому не будем.


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



Опять cl_waiting подскочил. Вэтот раз, правда, cl_acitve (жёлтое) не падает, а cl_waiting (красные точки, правая шкала) не поднимается выше 12.


Ну, это просто ошибка вконфигурации базы. Размер пула должен быть 24, именно такой пул выставлен всервисе. А настороне базы он остался равным 12. Исправляем, смотрим:



Открытый тест: 5000RPS / 140мс
Закрытый тест (100инстансов): 5440RPS / 94мс


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


Куда делась 1000 RPS


Итак, превышение размера пула сервиса надразмером пула БД вдва раза ведёт кпотере 20% пропускной способности (с5000RPS до4000RPS). Почему? И почему взакрытом тесте разница не видна?


Давайте разберём, что вообще происходит, когда сервис выполняет запрос через pgx. Вот мы посылаем некий запрос:


rows, err := h.db.QueryContext(ctx, `SELECT 1`)

h.db это пул соединений. Внутри QueryContext происходит Pool.Acquire(), который захватывает конкретное соединение длявыполнения нашего запроса. Соединений навсех не хватает, требуется синхронизация, длячего используется sync.Cond:


func (p *Pool) Acquire(ctx context.Context) (*Resource, error) {  //...  p.cond.Wait()  //...

sync.Cond внутри это пара атомиков, мьютекс и очередь насвязном списке (см. notifyList, который используется подкапотом уsync.Cond), то есть издержки насинхронизацию здесь минимальны. Горутина просто записывает себя вконец очереди и паркуется, ожидая, пока рантайм её разбудит. Ктому же всё это происходит настороне сервиса, который не является узким местом внашем случае.


Теперь представим, что пул сервиса больше, чем пул базы данных. Уpgbouncerа появляются 24клиентских соединения, но только 12серверных. Он вынужден жонглировать клиентскими соединениями, подключая их ксерверным поочередно. Это дорогая операция, т.к. нужно менять состояние серверного соединения. Вчастности, установить новые переменные, что требует общения сбазой через сокет попротоколу. И всё это происходит, внашем случае, вконтейнере сбазой, то есть мультиплексирование отъедает ресурсы убазы, которая и так является узким местом.


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


Разница результатов открытого и закрытого тестов


Почему разница не заметна взакрытом тесте, уменя точного ответа нет, но есть рабочая гипотеза.


Судя покоду find_server(), pgbouncer не стремится любой ценой подключить соединение ксерверному. Нашлось свободное серверное соединение хорошо, подключим. Нет придётся подождать. Этакая кооперативная многозадачность, вкоторой соединения не очень хотят кооперироваться.


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


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


Но это лишь правдоподобные рассуждения, хорошо бы их проверить. Когда-нибудь потом.


Запуск на холодную


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


Перезагружаем сервис и сервер сбазой, смотрим:



Разогнались до5000RPS за10секунд, примерно заминуту домаксимума. Значит, никакие механизмы прогрева кэша нам не нужны, можно сразу подавать боевой трафик.


Переезд pgx/v4, попытка номер два


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



Открытый тест: 5000RPS / 217мс, 5300RPS / 645мс
Закрытый тест: 5370RPS / 43мс


По производительности примерно то же самое, что и втретьей версии. Разница втом, как сервис деградирует призаведомо запредельной нагрузке. Счетвёртой версией это происходит медленнее.


Подбор размера пула в сервисе


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


Общее число соединений есть смысл выбирать винтервале отчисла ядер, доступных базе, доограничения начисло серверных соединений уpgbouncer (каким его выбрать вопрос дляDBA).


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


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


Не забываем


Закрывать результат


Это обсуждали множество раз, но тем не менее. Даже если вдокументации кбиблиотеке написано, что закрывать Rows необязательно, лучше всё же закрыть самому через defer.


rows, err := conn.Query(...)if err != nil {    return err}defer rows.Close()  // лучше закрыть принудительноfor rows.Next() {    // ...}if rows.Err() != nil {    return err}

Как только внутри цикла поrows.Next() случится паника или мы сами добавим туда выход изцикла rows останется незакрытым. Незакрытый результат это соединение, которое не может быть использовано другими запросами, но занимает место впуле. Его придется убивать потаймауту и заменять нановое, а это долго.


Быстрые транзакции


Применительно кpgbouncer: медленные транзакции забивают серверный пул.


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


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


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


Keepalive


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


Проверять гипотезы практикой


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


TL;DR, или выводы


  1. Прокачали сервис от50 до5000RPS, не применяя никакой особой магии.
  2. Мультиплексирование соединений вpgbouncerе поднагрузкой зло.
  3. Использовать всервисе пул большего размера, чем вбазе данных вредно.
  4. Выработать привычку делать транзакции быстрыми и закрывать результаты БД.

Благодарности


Кроме меня надзадачей работали коллеги изДоставки: Кирилл Любаев, Александр Кузнецов, Алексей Власов.


И огромное спасибо всем, кто помогал:
Андрею Аксёнову заидеи, что гео-индексы здесь не нужны, что инты рулят и вообще, проще лучше.
Павлу Андрееву, нашему DBA-инженеру, затерпение и оптимизацию настороне PostgreSQL.

Подробнее..

Масштабируем WebSocket соединения на Go

25.11.2020 14:11:26 | Автор: admin
Мессенджер Авито это:
  • 12 m уникальных пользователей в месяц;
  • Версии для всех современных платформ (Web, iOS, Android);
  • Достаточно нагруженное приложение около 800 тысяч подключений онлайн по WebSocket (основной протокол общения с пользователями).

Александр Емелин из компании Авито автор проекта Centrifugo open-source сервера real-time сообщений, где основной протокол передачи данных как раз WebSocket. Сервер используется в проектах Mail.Ru (в том числе в Юле), а также во внутренних проектах Badoo, ManyChat, частично Авито и за рубежом (например, Spot.im). Сейчас сервер базируется на доступной всем Go-разработчикам библиотеке Centrifuge.

На конференции Golang Conf 2019 Александр рассказал, как команда Авито решала проблемы при работе с WebSocket как про детали, касающиеся Go в частности, так и вообще про работу с большим количеством постоянных соединений.




Что такое WebSocket


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



WebSocket соединение стартует с HTTP запроса версии 1.1, и это важно, так как HTTP/2 не имеет механизма Upgrade. Существуют, конечно, спецификации, которые позволяют стартовать WebSocket и работать через HTTP/2, мультиплексируя WebSocket-соединения внутри отдельных стримов HTTP/2, но из драфта они так и не вышли.

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

Почему WebSocket


Почему вы вообще можете захотеть использовать WebSocket в своих приложениях:
  • Это достаточно простой протокол. Но дьявол, как известно, в деталях например, известный питонист Армин Ронахер в своей статье рассказывает о некоторых неочевидных переусложнениях WebSocket-протокола.
  • WebSocket работает на всех современных платформах и, что немаловажно, в браузере. Часто это становится решающим фактором, чтобы его выбрать как основной транспорт (ниже я расскажу чуть больше об альтернативах)
  • У WebSocket небольшой оверхед по сравнению с чистой TCP-сессией, то есть фрейминг WebSocket-протокола добавляет всего от 2 до 8 байт к вашим данным смотрите исследование на эту тему.


Задачи WebSocket-сервера


Я считаю, что WebSocket-сервер должен решать в реальном мире такие задачи:
  • Держать большое количество соединений;
  • Отправлять большое количество сообщений;
  • Обеспечивать fallback WebSocket-соединения. Но даже сейчас не все пользователи могут соединиться по протоколу WebSocket. Мы об этом сегодня поговорим.
  • Аутентификация соединений, причем как мы увидим ниже, WebSocket-приложения обладают своей спецификой.
  • Инвалидация соединений (иначе соединение может быть установлено один раз и висеть неделями).
  • Переживать массовый реконнект WebSocket-приложения отличаются от стандартных HTTP-серверов тем, что у вас масса постоянных соединений: не stateful-запросы, а stateful-приложения. Также есть проблема массового реконнекта от тысяч, сотен тысяч, миллионов пользователей.
  • Не терять сообщения при реконнекте. Если в чате/мессенджере потеряется сообщение при реконнекте, пользователи счастливы не будут. Часто восстанавливают состояние из основной СУБД приложения, но мы посмотрим на некоторые нюансы.

Понятно, что в зависимости от приложения важность того или иного пункта варьируется.

Тюнинг ОС основные моменты


Прежде, чем на ваш сервер придут тысячи соединений по WebSocket, вам нужно затюнить операционную систему.

Лимит файловых дескрипторов
Наверное, это первое, на что все натыкаются. Каждое соединение отъедает файловый дескриптор у ОС. По умолчанию лимит не такой уж большой от 256 до 1024 файловых дескрипторов. Хотите больше соединений поднимайте лимит.

Совет: Ограничивайте максимальное количество соединений. Если вы знаете, что у вас ОС не позволит принимать соединений больше, чем, например, 65535 (такой лимит выставлен), то не принимайте в своем WebSocket-сервере соединений свыше этого лимита:
// ulimit -n == 65535if conns.Len() >= 65500 {     return errors.New("connection limit reached")}conns.Add(conn)

Иначе, когда у вас все пойдет плохо (а оно пойдет), вы не сможете даже зайти в pprof для анализа проблемы, потому что поход в pprof это еще один файловый дескриптор, которого у вас нет. Вы также можете посмотреть в сторону netutil.LimitListener для этой задачи, но тогда pprof имеет смысл запускать на другом порту, отдельном от WebSocket сервера.

Ephemeral ports. Следующая проблема это эфемерные порты. Обычно проблема появляется на связке load balancer WebSocket-сервер и проявляется в том, что балансировщик не может открыть еще одно WebSocket-соединение до вашего WebSocket сервера, потому что у него исчерпались порты. Портов, с которых можно открыть соединение, по умолчанию не так много около 10-15 тысяч. Плюс у сокета есть состояние time wait, когда его нельзя переиспользовать смотрите, чтобы получить больше информации и узнать о способах решения.

Conntrack table. На каждой Linux-машине стоит iptables, в состав которого входит фреймворк netfilter. В нем каждое соединение трекается отдельной записью для контроля за тем, какие соединения установлены с сервером. Размер этой информации по умолчанию ограничен, как его затюнить смотрите здесь.

Sysctl. Возможно, вы захотите затюнить sysctl сетевой стек Linux размер памяти, которое отводится под TCP-соединения и многое другое. Об этом можно прочитать, например, здесь.

Что мы знаем о WebSocket в Go


Перейдем к уровню приложения (которое в нашем случае написано на Go). Что мы вообще знаем про WebSocket в Go:
  • Пакет websocket считается deprecated использовать его не рекомендуется.
  • Стандартом де-факто является пакет Gorilla Websocket практически все WebSocket-приложения на Go, которые сейчас есть, используют его для работы с WebSocket. Большинство моих примеров будут основаны именно на нём.
  • Библиотека от Сергея Камардина дает возможность делать некоторые оптимизации (в основном касающиеся эффективного использования оперативной памяти и оптимизаций аллокаций памяти при апгрейде соединения), на которые gorilla/websocket не способна.
  • Также есть библиотека от Anmol Sethi (nhooyr). Автор активен и даже сделал попытку мейнтейнить gorilla/websocket, чтобы новых пользователей перенаправлять на свою библиотеку :) Из преимуществ этой библиотеки можно отметить goroutine-safe API, встроенную поддержку graceful закрытия WebSocket-соединения (что достаточно нетривиально сделать с Gorilla WebSocket). Сейчас темп разработки этой библиотеки замедлился, а несколько неприятных багов в issue-трекере осталось. И несмотря на заявления автора о том, что пакет имеет внутри некоторые оптимизации на моих бенчмарках Gorilla WebSocket давал более производительный результат.


Простой WebSocket сервер


Посмотрим на простой WebSocket-сервер:
var upgrader = websocket.Upgrader{     ReadBufferSize: 1024,     WriteBufferSize: 1024,}func ServeHTTP(w http.ResponseWriter, r *http.Request) {     conn, _ := upgrader.Upgrade(w, r, nil)     client := newClient(conn)     defer client.Close()     go client.write()     client.read()}

Все начинается с того, что есть HTTP-обработчик. Как я сказал, WebSocket стартует с HTTP. Мы вызываем метод Upgrade:
conn, _ := upgrader.Upgrade(w, r, nil)

Внутри Upgrade библиотека gorilla/websocket делает Hack соединения, то есть берет его под свой контроль и отдает нам *websocket.Conn. На самом деле эта структура wrapper над сетевым соединением.

Далее мы передаем настройки upgrader: ReadBufferSize и WriteBufferSize. Это размер буферов, которые будут использоваться для ввода-вывода, для записи и для чтения для того, чтобы уменьшить количество системных вызовов (syscalls). И эти буферы будут прибиты к вашему соединению железно, то есть они увеличивают размер потребления памяти. Мы к этому еще вернемся.

Обычно мы оборачиваем соединение в какую-то нашу структуру уровня приложения, назовем ее client:
     client := newClient(conn)

В конце хендлера мы закрываем клиента и не забываем там же закрывать соединение:
     defer client.Close()

Стартуем отдельную горутину на запись (кстати нужно помнить о том, что gorilla/websocket не поддерживает конкурентное чтение и конкурентную запись единовременно только одна горутина может писать данные в соединение, и только одна горутина может читать из него):

     go client.write()

Блокируем HTTP хендлер методом read (read вычитывает данные из WebSocket до тех пор, пока не случится ошибка. Как только случается ошибка, вы выходите из цикла read и HTTP-хендлер завершается):
     client.read()


Оптимизации потребления RAM


Формула Камардина


В работе WebSocket-соединения есть известная проблема: они требовательны к оперативной памяти. Эта тема поднималась не так давно в GO-community Сергей Камардиным (Миллион WebSocket и pub/sub). Даже если тема WebSocket вам не интересна, рекомендую эту статью для ознакомления там в каждой строчке инженерная мысль. Сергей говорит о том, что потребление памяти на WebSocket-соединение складывается из следующих факторов:
  1. Две горутины (одна читает, другая пишет);
  2. HTTP-буферы на чтение и на запись от стандартной библиотеки Go, которая аллоцируется, потому что WebSocket-соединение начинается с HTTP;
  3. Буферы, которые мы в приложении используем для ввода-вывода.

Если аппроксимировать, то на миллион соединений нужно минимум 20 GB RAM. Достаточно большая цифра, не правда ли?



Также стоит посмотреть выступление Going Infinite, handling 1 millions websockets connections in Go израильтянина Eran Yanay, который по сути взял идеи Сергея и переложил их на иностранный лад, дописав примеры.

Но на самом деле это самый плохой сценарий. Плюс оперативная память стоит относительно дешево, и современный сервер, например, в Avito имеет 378 GB RAM. Такой сервер позволяет держать много соединений, и в большинстве случаев первым лимитирующим фактором для приложения станет CPU, а не RAM. Тем не менее, экономить память, конечно, нужно, и ниже мы посмотрим на кое-какие трюки с Gorilla WebSocket, которые позволят сократить потребление RAM.

Переиспользуем HTTP-буферы


С gorilla/websocket мы можем переиспользовать буферы, которые аллоцируются HTTP-сервером стандартной библиотеки. Для этого мы ReadBufferSize и WriteBufferSize передаем нулями:

var upgrader = websocket.Upgrader{     ReadBufferSize: 0,     WriteBufferSize: 0,}func ServeHTTP(w http.ResponseWriter, r *http.Request) {     conn, _ := upgrader.Upgrade(w, r, nil)     client := newClient(conn)     defer client.Close()     go client.write()     client.read()}  

Gorilla/websocket может переиспользовать буфер, используя Hijack соединения:
func Upgrade(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {      hj, ok := w.(http.Hijacker)      if !ok {          http.Error(w, "error hijacking", http.StatusInternalServerError)          return      }      conn, bufrw, err := hj.Hijack()      if err != nil {          http.Error(w, err.Error(), http.StatusInternalServerError)          return      }      ...}  

Если посмотреть внутрь функции Upgrade, то когда происходит Hack, возвращается объект bufio.ReadWriter, который содержит именно те буферы, которые HTTP-сервер аллоцировал для обработки изначального запроса:
     conn, bufrw, err := hj.Hijack()

При всем этом у нас все равно остаются две горутины, а буферы прибиты к соединениям.

Gobwas/ws: beyond std lib


Библиотека от Сергея Камардина позволяет отойти от этих проблем и сделать две оптимизации:
  1. Zero-copy upgrade. Мы убираем использование стандартного HTTP-сервера Go и сами парсим HTTP, при этом не аллоцируя дополнительной памяти.
  2. Возможность использовать epoll/kqueue (см. netpoll, gnet, evio, gaio), используя syscalls и минуя рантайм и Go netpoller. Тем самым можно даже отойти от двух горутин, одна из которых пишет, а другая читает. Также можно переиспользовать все буферы, о которых шла речь.

Кстати от буфера, прибитого на чтение к WebSocket-соединениям, может помочь избавиться issue на GitHub в репозитории Go: #15735 net: add mechanism to wait for readability on a TCPConn (правда, движения по ней в последнее время нет). Как только она закроется, у нас будет возможность буферы переиспользовать.

Write Buffer Pool


От Write Buffer Pool уже сейчас в gorilla/websocket мы можем избавиться, используя sync.Pool в upgrader. Тогда буферы на запись будут переиспользоваться:
var upgrader = websocket.Upgrader{     ReadBufferSize: 1024,     WriteBufferPool: &sync.Pool{},}     func ServeHTTP(w http.ResponseWriter, r *http.Request) {     conn, _ := upgrader.Upgrade(w, r, nil)     client := newClient(conn)     defer client.Close()     go client.write()     client.read()}  


Даем поработать GC


Есть еще один трюк, который со стандартной библиотекой Go позволит сократить потребление памяти мы даем поработать GC:
var upgrader = websocket.Upgrader{     ReadBufferSize: 1024,     WriteBufferSize: 1024,}func (hub *Hub) ServeHTTP(w http.ResponseWriter, r *http.Request) {     conn, _ := upgrader.Upgrade(w, r, nil)     ...     // Allow collection of memory referenced by the caller     // by doing all work in new goroutines.     go client.write()     go client.read()}  


Это значит, что в последний момент, когда происходит чтение, мы начинаем читать в отдельной горутине, и тем самым HTTP-хендлер завершается:

     go client.write()     go client.read()


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

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



А это профиль, когда мы стартовали отдельную горутину:



Кажется, что разница видна невооруженным глазом. Но если подсветить, то именно столько объектов garbage collector получает возможность собрать:



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

Отмена сontext


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



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

В данном случае контекст закроется сразу, как только завершится HTTP-хендлер. Но на самом деле это не проблема. Мы можем воспользоваться крутой фичей Go оборачивать интерфейсы и менять реализацию методов. Посмотрим на интерфейс Context:
type Context interface {     Deadline() (deadline time.Time, ok bool)     Done() <-chan struct{}     Err() error     Value(key interface{}) interface{}}  

Здесь нас в первую очередь интересует метод Done(), который у него есть. Нам нужно создать свой контекст:
type customCancelContext struct {     context.Context     ch <-chan struct{}}...func (c customCancelContext) Done() <-chan struct{} {      return c.ch}func (c customCancelContext) Err() error {     select {     case <-c.ch:         return context.Canceled     default:         return nil     }}  

Мы назвали его customCancelContext, и далее:
  • Обернули изначальный контекст:

context.Context

  • Передали в него канал, который будет закрываться тогда, когда соединение реально закроется (мы сами это знаем, сами управляем):

ch <-chan struct{}

  • Переопределили метод Done():

func (c customCancelContext) Done() <-chan struct{} {        return c.ch     }

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

Формат сериализации сообщений


Перейдем к тому, что надо рассылать много сообщений. Тут я Америку не открою вам нужно использовать эффективный формат сериализации. Выбор на самом деле большой, сами выбирайте:
  • JSON
  • Protobuf
  • Msgpack, CBOR
  • Свой протокол

Но помимо стандартных реализаций посмотрите также в сторону библиотек, которые генерируют код. Яркий пример gogo/protobuf, который позволяет ускорить сериализацию protobuf и сам по себе достаточно быстрый (в 5 раз), потому что использует не модуль reflect, а кодогенерацию:



Правда, на данный момент использовать gogo/protobuf я бы не рекомендовал. Подобные библиотеки есть и для Json, думаю, вы их знаете easyjson, ffjson, и на этом можно много сэкономить.

Инвалидация соединения


Еще одна проблема, которую мы в мессенджере Авито успешно побороли это инвалидация соединения. Вообще откуда эта проблема? Приходит WebSocket-соединение от пользователя. Оно устанавливается и может висеть неделю. Что может произойти? Пользователь может открыть несколько табов браузера и в одном из них разлогиниться. В админке могут заблокировать учетную запись пользователя за какое-то нарушение, а WebSocket-соединение продолжает жить. Какой тут может быть выход?
  • Push. Вы можете подписаться на события, если у вас есть какая-то шина данных. Например, у нас в Авито есть Kafka как шина данных. Мы подписались на такие события и отключаем WebSocket.
  • Pull. Но если такой шины данных нет, можно просто периодически проверять, что WebSocket-соединение активно (периодическая валидация). Это дороже, но тут нужно найти trade-off между производительностью и тем интервалом, с которым вы проверяете каждое висящее соединение.


Fallback для WebSocket


Не все пользователи могут подключиться по WebSocket даже сейчас. Проблема далеко не в том, что где-то все еще существуют старые браузеры (Internet Explorer 8, 9), которые не поддерживают WebSocket. Основные проблемы связаны с корпоративными пользователями. Работодатель ставит своим сотрудникам доверенный рутовый сертификат на компьютер, а далее имеет возможность перешифровывать TLS-трафик даже TLS-трафик! на своих прокси. Причем прокси режет WebSocket-соединение, намеренно или потому, что там старое ПО. Такие примеры сплошь и рядом (например, в банках).

Когда я работал в Mail.ru, был случай, когда браузерное расширение Adblock заблочило все WebSocket-соединения на домены и поддомены Mail.ru. Почему? Потому что мои коллеги рассылали через WebSocket-соединение рекламу. И нас спас тогда только HTTP-Fallback.

Самый простой способ добавить HTTP-Fallback в приложение на Go это библиотека SockJS-Go, и на стороне браузера использовать SockJS client. SockJS-Go это серверная реализация для клиента SockJS. Если нет соединения по WebSocket, то соединение будет установлено через один из HTTP-транспортов: XHR-streaming, Eventsource, XHR-polling и т.д. Недостаток все эти транспорты однонаправленные (сервер -> клиент), поэтому для работы с SockJS в двунаправленном режиме и масштабировании на несколько серверов придется еще включать sticky сессии на балансере.

Понятно, что fallback можно реализовать и вручную например, через long-polling. Это не так сложно сделать, и, если вы храните стрим сообщений в быстром хранилище, можно обойтись без sticky сессий (об этом мы еще поговорим далее).

Стоит упомянуть, что для общения между клиентом и сервером многие используют GRPC (правда в данный момент GRPC нельзя использовать из браузера без дополнительного прокси). В этом докладе мы GRPC обходим стороной, но для вашего сценария это может быть неплохим вариантом. Пока же для двустороннего общения клиента и сервера из браузера альтернатив WebSocket пока нет. Также при использовании WebSocket + Protobuf вы можете ожидать гораздо более низкое потребление CPU на сервере (смотрите статью про Centrifugo v2).

Интересной технологией, на которую стоит обратить внимание в будущем является WebTransport (начать знакомство можно здесь). Если коротко это эффективная клиент-сервер коммуникация поверх QUIC или HTTP/3 (который базируется на QUIC). Сейчас это еще драфты, но с технологией уже можно поэкспериментировать. Из самого вкусного у разработчиков появится возможность использовать UDP из браузера, что ранее было невозможно без WebRTC обвеса с его STUN, ICE и т.д.

Performance is not scalability


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

Горизонтальное масштабирование


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



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

Центральный брокер работает по модели PUB/SUB: когда соединения приходят на WebSocket-сервера, они инициализируют подписку на свой персональный топик в брокере. Брокер знает о всех таких подписках, и когда вы делаете публикацию сообщения, вы делаете ее через брокера. В этот момент через механизм PUB/SUB сообщение долетает только до тех WebSocket-серверов, до которых оно должно долететь, где есть клиенты, реально заинтересованные в этом событии. Далее оно доставляется по WebSocket клиенту:



Про центральный PUB/SUB-брокер для масштабирования WebSocket рассказывается в большинстве статей. Однако конкретики нет. Часто говорят: Ну, а для брокера используйте RabbitMQ или Redis, и на этом повествование завершается.

Я добавил больше конкретики в этот вопрос. Посмотрим, что нам нужно от брокера:
  • Производительность конечно, это первое требование, это логично.
  • Сохранение порядка сообщений тоже важное свойство, которое нам нужно.
  • Масштабируемость брокера. Мы хотим, чтобы брокер сам по себе масштабировался и был бы отказоустойчивым.
  • Миллионы топиков. Мы хотим, чтобы он поддерживал миллионы топиков одновременно, потому что это частый use case когда у каждого соединения есть свой персональный канал. Мы хотим рассылать сообщения конкретному пользователю. И если у вас миллион WebSocket-соединений, появляется миллион топиков в брокере.
  • Кэш/стрим сообщений. Мы хотим, чтобы брокер поддерживал кэш или стрим сообщений в топике/канале. Что это такое и от чего это спасает, поговорим чуть позже.
  • Возможность писать процедуры и это большой бонус. Я расскажу, как мне это помогло в Centrifugo и в мессенджере Авито.


Опции брокера сообщений


RabbitMQ


Он будет работать, пока у вас небольшая нагрузка. На каждое соединение вы создаете очередь. На самом деле это не масштабируется, когда появляется множество пользователей, когда большой коннект-дисконнект рейт, при этом вы часто делаете bind/unbind очереди. Например, когда я пришел в мессенджер Авито, там использовался RabbitMQ, и было 100 тысяч подключений. На 100 тысячах подключений RabbitMQ потреблял 70 CPU. Забегая вперед, мы заменили RabbitMQ на Redis, и получили 0,3 ядра CPU в 200 раз лучше для нашей задачи!

Kafka


Кажется вполне естественным попробовать Kafka для этого. Моё мнение, что это оверкилл для многих приложений, потому что Kafka сохраняет стрим на диск, а консьюмер Kafka работает по pull-модели, что может быть неэффективным, когда у вас миллионы топиков и вам надо пулить данные из всех. Kafka предпочитает более стабильную конфигурацию своих топиков, потому что в динамике, когда у вас появляется subscribe/unsubscribe, топики создаются и удаляются на лету, и кажется, что во многих случаях лучше с Kafka так не делать.

Pulsar


Это еще один кандидат, но мне сложно рассуждать о его преимуществах/недостатках, так как я с ним не работал. Возможно при условии использования in-memory очередей Pulsar может быть неплохим кандидатом.

Nats, Nats-streaming


Мне кажется, что Nats вы вполне можете использовать, если нужен unreliable at most once PUB/SUB. Это производительное решение, написанное на Go. Nats-streaming я бы не использовал, потому что посмотрел его код и в достаточно критичных для себя местах нашел todo, которые не имплементируют определенную логику при восстановлении сообщений. Для меня это показалось критичным.

Tarantool


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

Redis


Мы в мессенджере Авито остановились на Redis, потому что:
  • Redis производительный, в том числе у него производительный PUB/SUB;
  • Он стабильный и, самое главное, предсказуемый;
  • У него есть Sentinel для High Availability;
  • Он позволяет писать атомарные LUA-процедуры;
  • Структуры данных позволяют хранить кэш сообщений. Опять возник этот магический кэш сообщений, но мы к нему скоро вернемся.


Соединения между сервером и брокером


Тут есть несколько очевидных советов если вы делаете центральный PUB/SUB брокер, то:
  • Используйте одно или пул соединений между WebSocket-сервером и вашим брокером.
  • Не используйте новое соединение на каждый коннект! Я видел часто в примерах на GitHub, как пишут код: пришло новое WebSocket-соединение, открываем новое PUB/SUB соединение с Redis или с каким-то другим брокером. Так делать не надо, это антипаттерн и это не масштабируется.
  • Используйте максимально эффективный формат сериализации сообщений для общения между WebSocket-сервером и вашим брокером. Здесь не надо задумываться о том, чтобы формат был человеко-читаемым, потому что его не увидят ни ваши фронтенд-разработчики, ни тестировщики. Это сугубо внутренняя вещь, и вы можете делать ее максимально эффективной.

К брокерам мы еще вернемся. Поговорим о другой проблеме.

Массовый реконнект


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



Часто такая пиковая нагрузка это большая проблема. Посмотрим, как с ней можно бороться с помощью нескольких техник:

Exponential backoff на клиенте. Первая, всем известная практика это обязательный Exponential backoff на клиенте. Клиенты должны переподключаться с экспоненциально увеличивающимися интервалами.

Rate limiter на WebSocket-сервере. Если вы знаете, что у вас на бэкенде какой-то ресурс деградирует при конкурентном доступе, поставьте туда самый простейший bounded семафор и ограничьте конкурентный доступ. Делается на Go элементарно канал с буфером, и все. Зато у вас не деградирует система. То, что пользователь, предположим, переключится чуть позже обычно не так критично.

JWT-аутентификация как способ не нагружать бэкенд сессий. Часто происходит следующее. Приходит коннект на сервер, его нужно аутентифицировать, а аутентифицируете вы его через бэкенд сессий. Причем бэкенд сессий может быть развернут как локально, так и отдельно в виде сервиса. JSON Web Token позволяет избавиться от большой нагрузки на бэкенд сессий, потому что у JWT есть expiration time.

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



Добивайтесь максимальной производительности соединений. У вас сотни тысяч пользователей приходят и хотят переподписаться. В этот момент вы точно хотите переподписать их как можно скорее. Это хорошо и вам, и пользователям. Например, в случае с Redis мы можем это делать, используя Redis пайплайнинг. Это отправка нескольких запросов Redis за один запрос. Мы это делаем и в Centrifugo, и мессенджере Авито. Мы используем пайплайнинг на полную в этот момент. С этим помогает техника Smart batching паттерн, который позволяет из нескольких независимых источников горутин собирать запросы и делать один batch запрос:

maxBatchSize := 50for {     select {     case channel := <-subCh:         batch := []string{channel}     loop:         for len(batch) < maxBatchSize {              select {              case channel := <-subCh:                     batch = append(batch, channel)              default:                     break loop              }          }          // Do sth with collected batch  send          // pipeline request to Redis for ex.      }} 


У нас есть каналы, из которых приходит какая-то работа. Мы знаем, что эту работу эффективней выполнять пачками. Мы читаем из канала, записываем объект, который нам нужно обработать, в batch (например, в слайс):
batch := []string{channel}

Далее продолжаем вычитывать из этого канала данные до тех пор, пока batch не перерастет свой максимальный размер или пока в канале не останется данных:
for len(batch) < maxBatchSize {     select {     case channel := <-subCh:

Select в Go это позволяет сделать. В итоге копим batch:
batch = append(batch, channel)


На выходе у нас получается пачка объектов, с которыми мы можем работать, например, послать пайплайн запрос в Redis. Пример со smart-batching на play.golang.org. Также в моем репозитории на GitHub вы можете посмотреть, как Smart batching и пайплайнинг помогают во взаимодействии с Redis там есть набор бенчмарков.

Кэш сообщений, чтобы убрать пиковую нагрузку с СУБД. Как я уже сказал, пользователи хотят восстановить свое состояние и не пропускать сообщения, которые были в момент реконнекта. Вы можете хранить стрим сообщений, которые опубликовали, в каком-то быстром и горячем кэше. Мы их храним в Redis в структуре данных LIST. Как вариант, начиная с Redis 5.0, можно использовать Redis STREAM. Происходит публикация, идет запрос в Redis. Там работает LUA процедура, которая нам позволяет атомарно, в один RTT, сделать следующее:



  1. Мы добавляем сообщения в структуру данных List. Как вы видите, здесь уже три сообщения. У каждого сообщения при этом есть инкрементальный номер. Мы его увеличиваем атомарно.
  2. Далее мы публикуем сообщение в PUB/SUB Redis и в этот момент оно улетает в PUB/SUB. В свою очередь, PUB/SUB долетает до клиента (если он подключен). Как только клиент реконнектится, они передают номер сообщения, которое видели последним.
  3. Идем в Redis, смотрим в Lists. Если есть сообщения, восстанавливаем их клиенту из этой структуры данных, как будто он даже не был отключен.
  4. Так клиент получает весь стрим сообщений, которые ему были высланы в интервале отсутствия и так снимается пиковая нагрузка с СУБД.

На самом деле Redis PUB/SUB это at most once гарантия доставки. В мессенджере Авито и в Centrifugo мы дополнительно на уровне кода приложения нивелируем потери, сверяя номер входящего PUB/SUB сообщения с ожидаемым, а затем периодически синхронизируя позицию клиента с ожидаемой в случае долгого отсутствия сообщений из PUB/SUB. Если обнаружили потерю закрываем соединение клиента, давая ему переподключиться и выполнить всю необходимую логику для восстановления состояния.

На таком стриме можно сделать и fallback для WebSocket. В мессенджере Авито мы так и делаем. У нас есть стрим таких сообщений для каждого топика, и когда наш пользователь приходит, мы используем HTTP polling для fallback, чтобы он забрал данные из этого стрима и отдал их клиенту. Как бонус от кэша сообщений на этом же механизме можно построить и long-polling.

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



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



Centrifugo


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



Как я говорил, основа сервера библиотека Centrifuge, хотя это уже не совсем библиотека, а уже к фреймворку, потому что Centrifuge задумывается о масштабируемости за вас, имеет встроенный Redis и кэш сообщений. Плюс диктует клиент-серверный протокол. Так как Go-разработчики (и я в том числе) не очень любят фреймворки, то проще всего ее описать как альтернативу socket.io на Go со всеми сопутствующими за и против.

Демо стенд


Чтобы не быть голословным, я сделал демо-стенд на миллион WebSockets в Kubernetes, который использует библиотеку Centrifuge на сервере. На этом стенде я добился следующих цифр:
  • 1 mln соединений;
  • 30 mln сообщений в минуту, которые будут доставлены пользователям (это 500 тысяч сообщений в секунду);
  • 200 k коннектов в минуту приблизил к тем цифрам, которые у нас в мессенджере Авито есть;
  • 200 ms latency доставки в 99 персентиле.

Хочу подчеркнуть, что это не нагрузочное тестирование, где мы доводим систему до полки (отказа), а просто демо-стенд. У меня было ограниченное количество железа и данные цифры это далеко не потолок возможного масштабирования, поэтому был небольшой time-lapse. Что мы видим?

Number of connections быстро дорастает до миллиона. Мы ждем какое-то время, и видим, что система ведет себя стабильно. Затем начинаем публиковать сообщения (график Messages delivered) и доходим до 30 млн сообщений в минуту. При этом смотрим на серверный CPU, на память, и в том числе на брокер (Redis). В данном случае используется 5 инстансов Redis, мы шардируем Redis по имени топика (консистентное шардирование jump). Мы видим, что latency дорастает до 200 мс и там примерно останавливается, когда рейт сообщений достиг 30 млн:



Чуть больше цифр о использованных ресурсах:
  • 40 ядер CPU total 20 подов в Kubernetes (~ 2 ядра CPU каждый);
  • 27 GB RAM total;
  • 32% ядра CPU утилизировано на каждом из 5 инстансов Redis;
  • 100 mbit/sec rx и 150 mbit/sec tx на каждом из подов.

Чуть подробнее про стенд можно почитать здесь.

Takeaways


Итак, что вы можете использовать:
  • Дайте GC поработать на благо потребления RAM, сказав: Старичок, приберись за нами! Мы тут немножко намусорили. И чем раньше мы это сделаем, тем лучше. Ему (garbage collectorу) все равно работать после того, как соединение завершится, поэтому не откладывай на завтра то, что можно сделать сегодня.
  • Используйте эффективный и компактный формат сериализации.
  • Используйте брокер сообщений для горизонтального масштабирования. Причем выбирайте тот, что подходит именно для вашей задачи.
  • Подумайте над необходимостью HTTP-fallback.
  • Лавину реконнектов нужно и можно переживать.
  • Посмотрите в сторону Centrifugo. Если вам нужно рабочее решение, сейчас вторая версия очень даже хороша JSON протокол, protobuf протокол, масштабируется с Redis.


Как со мной связаться: GitHub, Telegram, Facebook.

Конференция по Golang пройдёт уже в 2021 году, за новостями можно следить в Телеграм-канале. А пока в оставшиеся дни ноября и в декабре этого года у нас будет целая серия онлайн митапов. Ближайшие:

Следите за новостями Telegram, Twitter, VK и FB и присоединяйтесь к обсуждениям.
Подробнее..

Как устроены технические стажировки Авито

23.12.2020 14:04:12 | Автор: admin

Несколько раз вгод мынабираем стажёров втехнический департамент Авито. Они работают отшести месяцев догода ирешают боевые задачи. Поитогам большинство стажёров переходят вштат.


Нас зовут Станислав Юрков иИрина Мулёва, мынабираем стажёров ипомогаем имвпроцессе обучения. Встатье мырасскажем, как устроены технические стажировки иответим начастые вопросы кандидатов.



Накакие направления мынанимаем стажёров


Авито берёт стажёров понаправлениям Frontend, Backend, Mobile под iOS иAndroid, атакже вQuality Assurance. В2020-2021 году больше всего внимания мыпланируем уделить направлениям Backend иFrontend, так что расскажите онас заинтересованным друзьям.


Унас уже открыты несколько стажёрских позиций, нокаждый месяц будут добавляться новые. Все вакансии опубликованы наотдельной странице.


Кроме разработчиков, мыпериодически ищем стажёров-аналитиков понаправлениямBI иData Science. Оних вэтой статье мырассказывать небудем, ноактуальные позиции тоже будут появляются пообщей ссылке выше.


Условия стажировки


Сейчас мысобеседуем иадаптируем новых сотрудников удалённо. Работаем тоже издома. За2020год, несмотря накоронавирус, мынаняли 11стажёров втехнический департамент иуже перевели вштат некоторых изних. Иногда мыустраиваем встречи вофисе небольшим составом, чтобы улюдей была возможность познакомиться лично, ноосновная коммуникация происходит онлайн.



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


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


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


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


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


Техническая стажировка оплачивается. Мыплатим 40000 рублей довычета налога это 34800 рублей накарту вмесяц. Устажёров других направлений размеры оплаты отличаются, поэтому уточняйте ихнасобеседовании.


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


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


Как попасть настажировку


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


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


Посмотреть примеры тестовых можно внашем аккаунте наГитхабе:



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


Минутка статистики


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


  • Заявки свыполненными тестовыми заданиями: 150.
  • Собеседования: 50.
  • Выбрали стажёров втехнологические команды: 7.

Большинство стажёров изпрошлого набора уже работают вштате джуниор-разработчиками.


Что говорят стажёры опрограмме


image


Аделина Загитова, QAиbackend-стажёр, перешла вштат

Яискала хорошую подработку попрофессии, где можно набраться опыта параллельно сучёбой. Кроме Авито рассматривала Яндекс, Хоум Кредит Банк, Лабораторию Касперского, Wildberries. Выбрала Авито, потому что сомной провели отлично продуманное собеседование ипредложили самые удобные условия работы.

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


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

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

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



image


Владимир Нюхтилин, iOS-стажёр, перешёл вштат

Ясчитаю, что возможностью попасть втакую компанию как Авито, точно нужно пользоваться. Программа стажировки как раз про это.

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

Помоим наблюдениям, программа стажировок отлажена на150% благодаря многим тонкостям, иона постоянно развивается, как ивсё здесь. Яполучил большой опыт вАвито ипродолжаю работать над собой, решая задачи, которые стоят вцелях моей команды. Поэтому навопрос Что именно дала мне стажировка вАвито? ябы ответил так: Возможность для отличного, если нелучшего, старта карьеры вIT.



image


Никита Васильев, backend-стажёр, перешёл вштат

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

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



image


Артём Ольков, стажёр вSecurity, перешёл вштат

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

Когда ятолько пришёл настажёрскую программу, тоожидал, что смогу поработать над интересными задачами вдружной команде, повысить уровень нетолько hard, ноиsoft skills, окунуться вжизнь крупной, высокотехнологичной компании. Нопрограмма превзошла даже мои, как ядумал, завышенные ожидания. ВАвито яосвоил новые технологии, которыми никогда непользовался, познакомился смножеством увлечённых своим делом людей исформировал для себя дальнейший вектор развития.



image


Константин Голик, frontend-стажёр, перешёл вштат

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



Что говорят наставники


image


Ионов Владимир, Teamlead

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

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

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

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

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

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

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



image


Костас Кряров, Senior Frontend Engineer

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

Наставничество научило меня отцовскому духу. Я очень хотел, чтобы стажёру понравилось унас в команде и компании вцелом, чтобы комфортно работалось.

Мы даём стажёрам разные задачи. Сначала довольно простые, чтобы человек пощупал наши процессы, code-review и прочее. С развитием стажёра, задачи становятся сложнее:
  1. Вёрстка лендинга снуля.
  2. Рефакторинг тестов, чтобы стажёр научился работать сjest и нашими внутренними библиотеками, связанными стестированием.
  3. Интеграция новой функциональности вReact-Redux приложение.
  4. Умоего стажёра была интересная задача наисследование рендеринга графа статусов наsvg или canvas, учитывая положение рёбер и вершин графа.



image


Артём Пескишев, Senior Mobile Engineer

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

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

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

Мой стажёр быстро вник впроект, и спустя пару месяцев уже был полноценным участником команды, который работал надзадачами наравне совсеми, планировал свою работу и выполнял её взаданные сроки.



Вместо вывода


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

Подробнее..

CICD монолита Авито от коммита до моржа

03.06.2021 12:19:11 | Автор: admin

Всем привет, меня зовут Александр Данковцев, я lead engineer команды Antimonolith. В этой статье я расскажу, как построен CI/CD монолита Авито. Речь пойдёт про нашу архитектуру стейджинга, pre-receive хуки, то, что из себя представляет сборка и деплой, как устроен прогон автотестов и какие проверки происходят на merge. А ещё рассмотрим after-merge actions.

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

  • Релиз-кандидат версия кода, которому предстоит пройти процесс валидации тестов и деплой в продакшн при успехе.

  • Срез релиз-кандидата Git-ветка, созданная от мастера в процессе запуска сборки релиза.

Архитектура стейджинга Авито

Начнём со схемы архитектуры стейджинга. Это то, где вращается сам сайт Авито и компоненты, из которых он состоит:

Авито сайт, как и любой другой микросервис в стейджинг-окружении, деплоится встейджинговый Kubernetes-кластер. У нас есть отдельный namespace avito-site-tests, вкотором расположены ресурсы сайта: базы данных, баунсеры, сервис очередей, Sphinx, прочие репликации, всякие демоны. Это сделано для того, чтобы экономить ресурсы вкластере. Непосредственно каждая ветка сайта деплоится в собственный отдельный namespace, и все бэкенды натравливаются на ресурсы, которые расположены в отдельном неймспейсе. Особняком стоит Frontend Delivery Network (FDN), куда сгружается статика сайта.

Эта инфраструктура очень сложно деплоится: раскатить весь стейджинг одной кнопкой нельзя. Поэтому стейджинг поделен на Helm-релизы.

Отдельным релизом мы катим сами ресурсы, потому что это требуется очень редко. Ресурсы это база данных, Sphinx, Redis-ы, RabbitMQ. Также отдельно по срезу релиз-кандидатов или по требованию обновляются демоны монолита. На схеме это PGQ daemons, DataBus consumers и QaaS consumers. Мы вынесли их в отдельный деплой, чтобы перераскатка не пересоздавала базу данных, потому что база достаточно большая (около 10 Гб) и её пересоздание это сброс всех данных, созданных в процессе её работы, т. е. пропадут все созданные объявления и прочие ресурсы. Мы получим неконсистентное состояние с другими сервисами.

Деплой ресурсов стартует в 7 утра, когда все разработчики ещё спят, и к утру у нас есть готовая инфраструктура. Это позволяет нам тестировать в том числе и асинрохронное взаимодействие компонентов сайта. После раскатки ресурсов происходит пересоздание релиза кронов, демонов, консумеров.

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

Каждый бэкенд сайта Kubernetes Pod состоит из пяти контейнеров:

Nginx это точка входа плюс некоторая отдача статики.

Php-fpm сам бэкенд.

Envoy-core, который отвечает за прокси в сервисы, выполняет роль DNS resolve и keepalive. Например, мы можем стучаться по http://localhost:8888/service-item и попасть в service-item. Это всё нужно для большей производительности стейджинга.

Netramesh добавляет контекст в исходящие запросы: заголовок X-Source. Он также выполняет роль динамической подмены upstream-а сервисов по входящему заголовку X-Route. Формат такой: X-Router: src_host:src_port=dst_host:dst_port.

Navigator межкластерный маршрутизатор, который резолвит ClusterIP в набор PodIP во всех доступных дата-центрах.

Посмотрим на сетевой стек стейджинга. Когда мы запрашиваем произвольную ветку сайта, запрос проходит через фронденд nginx. В нашем случае это какой-то из подмножества avi-http, который отправляет запросы на Ingress. С Ingress мы попадаем в routing-gateway. Routing-gateway занимается подмешиванием заголовка X-Route и дальше прокидывает запрос в API-gateway. API-gateway балансит, куда отправить запрос: либо это будет Avito Site, либо какой-нибудь API-сomposition. API-сomposition это десктоп-сайт на Node.js или гошный сервис, либо любой другой сервис на любом другом языке.

Quality gates

Когда мы в общих чертах поняли, как выглядит стейджинг, рассмотрим quality gates, которые применяются для Avito Site.

Основные этапы прохождения коммита таковы:

  1. Гитовые pre-receive хуки.

  2. TeamCity: CI/CD прогоняет тесты, линтеры и так далее.

  3. Проверки на merge, флаги и обязательности.

  4. Действия после merge.

Давайте же покоммитим в монолит. Предположим, мы решили что-то поправить.

Pre-receive хуки

Pre-receive хуки помогают проверить, всё ли в порядке с коммитом до его пуша в репозиторий. Таких хуков у нас не очень много.

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

Плюс есть проверка на то, что коммит наш доменный, он принадлежит Авито, а не какой-то левый.

Что происходит в TeamCity

Вот максимально упрощённый граф зависимостей нашего CI/CD, я собрал основные этапы прохождения:

У нас есть всякие схема-чеки, DI-чеки, юнит-тесты, но лишь вершина айсберга. Из всей схемы я рассмотрю самый основной и сложный процесс прохождение end-to-end тестов от сборки Docker до прогона тестов.

Сборка

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

В сборке образа мы используем паттерн build-образ и push-образ. Build-образ большой и тяжёлый, с кучей зависимостей, сишных библиотек и прочих утилит, которые нужны, чтобы собирать непосредственно код. Для рантайма же этих зависимостей не должно быть. То есть унас собирается некоторый артефакт на этапе build code, после этого сбилженная статика загружается в Ceph. А остальная часть собранного кода запекается в Docker-образ и позже пушится.

Вот как происходит build code:

  1. Установка composer-зависимостей.

  2. Кодогенерация, например, генерация client-shop.

  3. Сборка DI, у нас используется Symfony.

  4. Сборка фронденда. Собирается npm, собирается Twig в кэши.

  5. Деплой хранимых процедур. Мы получаем свободного юзера, назначаем ему схему, search pass, и накатываем туда хранимки.

  6. Сборка словарей файловое представление справочных данных из базы или сервисов.

  7. Если всё прошло успешно, последний этап сборка артефактов, то есть генерация app.toml, swagger, rev.txt. Rev.txt это идентификатор сборки, окружение, в котором она собиралась, и прочая отладочная информация.

Деплой

Когда образ собрался и запушился, приходит следующая TeamCity сборка деплой.

Процесс следующий:

  1. Деплой в Kubernetes-кластер.

  2. Валидация доступности хоста: делается curl с небольшим прогрессирующим шагом, пока хост не станет отдавать код 200. Helm предоставляет информацию о том, что он успешно раскатился, проходят health-чеки и так далее.

  3. Регистрация хоста в routing-gateway.

  4. Автоматическая отбивка в Slack, о том, что хост собрался.

Автотесты

Хост собрался, самое время запускать end-to-end тесты. Если говорить простым языком, у нас сначала отрабатывает сборка Build E2E. В отдельный архив собирается папка с тестами и пушится в Artifactory. От E2E-тестов идёт много связей, я приводил их на первой картинке проTeamCity.

Для простоты рассмотрим одну из них E2E Test Suite. В этом билде настроено, какой тип тестов запускать, допустим, web или api, или заданы какие-то дополнительные параметры, например, относящиеся к конкретному юниту Авито. Этот билд общается с сервисом параллельного запуска автотестов: посылает ему задачу, сервис её исполняет и получает ответ.

Если копнуть немного глубже, то билд, прогоняющий тесты, представляет собой TeamCity meta runner, который через Docker запускает небольшое приложение, где реализован клишный parallel-client. Parallel-client делает запрос в parallel manager и передаёт ему все метаданные сборки: какие тесты к какому юниту относятся, какой артефакт был запушен в Artifactory. Parallel manager, получив результат, перекладывает всё в очередь и отвечает клиенту, что всё окей, я принял результат. После этого клиент начинает периодически поллить parallel manager на информацию о том, прошли тесты или нет.

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

Тесты ходят в Selenium, который ходит по сайту через Firefox, Chrome или любой другой браузер. Тесты также пользуются файловой системой (fs-qa), куда сохраняют скриншоты, html-странички и прочее. На результат выполнения смотрит воркер: для успеха код ответа 0, для провала 1.

После этого parallel worker пушит каждый выполненный кусочек задачи в отдельную очередь результаты выполнения тестов. Эту очередь слушает parallel manager. Когда менеджер получает результаты, он отгружает их в tests reporters backend, где хранятся отчёты о том, что такой-то тест столько-то выполнялся и прочая информация.

Parallel client через cli-команду запрашивает у test reporter backend финальную информацию отом, что все тесты пройдены. В ней отмечено, сколько тестов прошли, например, 150 из 170. На основе этой информации билд становится зелёным или красным. Также после получения этой информации в TeamCity создаётся артефакт со ссылкой на frontend test reporter. Мы можем зайти в него из TeamCity и визуально посмотреть полную отчётность о том, какие тесты прошли и сколько они выполнялись.

Вот как весь процесс выглядит схематически:

Каждый Е2Е-тест создаёт свою коллекцию тестов, а каждая коллекция тестов создаёт свои репорты, и их довольно много. На прогоне ветки для сайта у нас получается семь Е2Е-сьютов, а для релиза их порядка 50 штук. Когда все отчёты собрались и пришли отбивки в stash, мы можем зайти в stash и замерджить ветку.

Проверки на merge

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

Merge-плагин анализирует diff, то, какие файлы были изменены, и выставляет флаги для текущего pull request. Если в pull request были изменения PHP-файлов, он ставит backend changes, если были изменения JS или CSS frontend changes. Маркируются и случаи, когда произошли изменения в папке с Е2Е-тестами, например, buyer test changes. В итоге работы плагина у pull request появляются флаги, что в нём потрогали: frontend, backend и тесты байеров.

Наименование флага

Значение

Условие

Backend changes

Yes

*.php

Frontend changes

No

*.css, *.js

Buyer E2E changes

No

tests/e2e/BuyerTests/*

DataBase changes

Yes

*.sql

Также у нас есть карта всех билдов, которые мы хотим валидировать на pull request Avito Site. Но мы не хотим, чтобы каждое изменение в read.me порождало требование пройти 30проверок. Мы хотим, чтобы если потрогали какой-нибудь текстовый файлик, было наличие только одного апрува. Это достигается тем, что каждая проверка маркируется списком флажков, на которые она должна срабатывать.

Наименование линтера

Список флажков

UnitTests

Backend changes

FrontendCI

Frontend changes

DockerBuild

Backend changes, Frontend changes, Database changes

BuyerE2E Suite

Buyer E2E changes

Например, для "BuyerE2E Suite" проверка прогоняется в том случае, если на pull request был выставлен флаг "Buyer E2E changes". Если мы не трогали папку с байерами, то проверки не будут обязательными, на pull request не будет требоваться, что они должны быть зелёными. Если потрогали SQL-ки, значит, обязательно должны пройти интеграционные тесты на базе, и так далее.

Действия после merge

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

Её реализует плагин в нашем stash, который слушает изменения на merge и триггерит соответствующие сборки в TeamCity. Прежде всего, он делает удаление хоста, что экономит нам ресурсы: Kubernetes не резиновый. Ceph тоже не резиновый, поэтому после мерджа происходит очистка статики. И в конце плагин снимает регистрацию с routing-gateway.

Теперь всё, мы в мастере.

А что с релизами?

В статье я не стал рассматривать релизы Авито. Они, в принципе, очень похожи на описанный выше процесс. В релизах просто происходит не pull request в мастер, а срез ветки. Востальном смысл тот же: сборка сайта, прогоны Е2Е-тестов, просто их больше, а вместо проверки в stash на merge-check валидация релиз-инженерами.

Подробнее..

Вам показалось! Все о Perceived Performance

21.01.2021 14:15:10 | Автор: admin

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

В большинстве случаев с ростом реальной производительности улучшается и Perceived Performance. А когда реальная производительность не может быть с легкостью увеличена, существует возможность поднять видимую. В своем докладе на Frontend Live 2020 бывший разработчик Avito Frontend Architecture Алексей Охрименко рассказал о приемах, которые улучшают ощущение скорости там, где ускорить уже нельзя.

Производительность обязательное условие успеха современных Web-приложений. Есть множество исследований от Amazon и Google, которые говорят, что понижение производительности прямо пропорционально потере денег и ведет к слабой удовлетворенности вашими сервисами (что снова заставляет терять деньги).

Первопричина полученного стресса ожидание. Научно доказано, что оно может вызывать:

  • беспокойство;

  • неуверенность;

  • дискомфорт;

  • раздражение;

  • скуку.

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

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

Не игнорируйте этот момент: вы обязательно должны его учитывать.

Для этого есть множество разнообразных техник, в том числе:

  • lighthouse;

  • devTools profiler.

Но даже если все сделать идеально, этого может оказаться недостаточно.

Есть интересная история про аэропорт Huston. Она отлично вписывается и в современные реалии разработчиков.

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

Люди часто жаловались на долгое ожидание багажа в аэропорту. И инженеры аэропорта Huston не спали ночами, работали сверхурочно, но реализовали выдачу багажа всего за 8 минут. Однако клиенты все равно продолжали жаловаться.

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

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

Perceived Performance

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

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

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

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

Все дальнейшие примеры будут для Angular приложений, но, несмотря на это, они применимы к любому современному фреймворку. Приступим!

Не блокируйте пользователя

Первый совет: ни в коем случае не стоит блокировать пользователя. Вы можете сейчас сказать: Я и не блокирую!.

Попробуйте узнать себя в этом примере:

Допустим, мы кликаем на удаление какого-то элемента, запрашиваем данные, после чего удаляем.

Все равно не узнаете? А так?

Спиннеры это не выход! Хоть они и могут стать ленивым способом разобраться с неудобной ситуацией.

Что можно предложить в качестве альтернативы спиннеру?

Можно нажать на кнопку УДАЛИТЬ и показать статус этой кнопки (item удаляется только для одного элемента), не демонстрируя спиннер. В дальнейшем можно отправить запрос на сервер, и когда он придет, показать, что элемент удалился, либо при ошибке передать данные о ней. Вы можете возразить: Но я могу делать только 1 запрос за раз! это ограничение бэкенда. С помощью RxJs и оператора concat можно буквально одной строчкой кода создать минимальную очередь:

Более серьезная имплементация, конечно, займет больше, чем одну строчку.

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

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

В Angular есть ngx-spinner, который поддерживает такой функционал.

Это, как минимум, уменьшит время ожидания, и пользователь сможет в этот момент сделать хоть что-то.

Обманывайте

Обман зачастую базируется на иллюзиях скорости.

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

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

Где можно применить такую методологию?

  • Progress Bar;

Есть исследование, показывающее, что если добавить полоски, которые идут в обратном направлении внутри Progress Bar, то визуально он выглядит как более быстрый. В такой настройке можно получить до 12% ускорения, просто применив дополнительный скин. И пользователи воспримут работу, прошедшую под этим Progress Bar, на 12% быстрее. Вот пример того, как можно реализовать такой слайдер на CSS.

  • Скелетон;

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

Скелетон это некое схематическое отображение сайта до момента его загрузки:

В этом примере мы видим, где будут расположены чаты.

Существует исследование, которое показывает, что люди воспринимают скелетоны быстрее от 10 до 20%.

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

Существует огромное количество нужных компонентов для Angular, React, View. К примеру, для Angular есть skeleton-loader, в котором можно прописать внешний вид и сконфигурировать его. После чего мы получим наш скелетон:

  • Экспоненциальная выдержка.

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

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

Это одна из best practice в энтерпрайз-приложениях, потому что бэкенд может не работать по разным причинам. Например, происходит деплой или хитрая маршрутизация. В любом случае очень хорошее решение: попробовать повторить. Ведь никакое API не дает 100% гарантии, 99,9% uptime.

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

Но даже с этим сценарием мы сами себе можем сделать DDOS (Distributed Denial of Service). На это попадались многие компании. Например, Apple с запуском своего сервиса MobileMe.

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

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

Best practice: применять exponential backoff. В rxjs есть хороший дополнительный npm пакет backoff-rxjs, который за вас имплементирует данный паттерн.

Имплементация очень простая, 10 строчек кода. Здесь вы можете обойтись одной. Указываете интервал, через который начнутся повторы, количество попыток, и сбрасывать ли увеличивающийся таймер. То есть вы увеличиваете по экспоненте каждую следующую попытку: первую делаете через 1 с, следующую через 2 с, потом через 4 с и т.д.

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

Следующий совет очень простой добавить Math.random() для initialInterval:

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

Предугадывайте!

Как уменьшить ожидание, когда невозможно ускорить процесс?

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

  • Предзагрузка картинок;

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

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

  • Предзагрузка 18+

Наверняка вы сталкивались в HTML со стеком link, который позволяет переподключить stylesheets:

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

Можно указать атрибут rel ="preload", сослаться в ссылке на наш элемент (href="styles/main.css"), и в атрибуте as описать тип предзагружаемого контента.

  • prefetch.

Еще один вариант это prefetch:

Главное запомнить, что preload и prefetch два самых полезных инструмента. Отличие preload от prefetch в том, что preload заставляет браузер делать запрос, принуждает его. Обычно это имеет смысл, если вы предзагружаете ресурс на текущей странице, к примеру, hero images (большую картинку).

ОК, это уже лучше, но есть одна маленькая проблема.

Если взять какой-нибудь среднестатистический сайт и начать префетчить все JavaScript модули, то средний рост по больнице составляет 3 МБ. Если мы будем префетчить только то, что видим на странице, получаем примерно половину 1,2 МБ. Ситуация получше, но все равно не очень.

Что же делать?

Давайте добавим Machine Learning

Сделать это можно с помощью библиотеки Guess.js. Она создана разработчиками Google и интегрирована с Google Analytics.

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

При этом эта библиотека будет точна на 90%. Загрузив всего 7%, она угадает желания 90% пользователей. В результате вы выигрываете и от prefetching/preloading, и от того, что не загружаете все подряд. Guess.js это идеальный баланс.

Сейчас Guess.js работает из коробки с Angular, Nuxt js, Next.js и Gatsby. Подключение очень легкое.

Поговорим о Click-ах

Что можно сделать, чтобы уменьшить ожидание?

Как предугадать, на что кликнет пользователь? Есть очевидный ответ.

У нас есть событие, которое называется mousedown. Оно срабатывает в среднем на 100-200 мс раньше, чем событие Click.

Применяется очень просто:

Просто поменяв click на mousedown, мы можем выиграть 100-200 мс.

Мы можем заранее предсказать, что пользователь кликнет на ссылку.

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

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

Есть библиотека, которая анализирует скорость, и благодаря этому может предсказать, куда я кликну.

Библиотека называется futurelink. Ее можно использовать абсолютно с любым фреймворком:

Вы передаете ей те DOM элементы, которые должны участвовать в прогнозе. Как только библиотека предсказывает нажатие на элемент, сработает callback. Вы можете его обработать и начать загружать страницу или залогинить пользователя заранее.

Что пользователь хочет получить при переходе на страницу? В большинстве сценариев: HTML, CSS и немного картинок.

Все это можно реализовать за счет серверного рендеринга SSR.

В Angular для этого достаточно добавить одну команду:

ng add @nguniversal/express-engine

В большинстве случаев это работает замечательно, и у вас появится Server-Side Rendering.

Но что, если вы не на Angular? Или у вас большой проект, и вы понимаете, что внедрение серверного рендеринга потребует довольно большого количества усилий?

Здесь можно воспользоваться статическим prerender: отрендерить страницы заранее, превратить их в HTML. Для этого есть классный плагин для webpack, который называется PrerenderSPAPlugin:

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

Но вы можете сделать все еще проще: зайти в свое SPA приложение и написать:

document.documentElement.outerHTML,

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

Заключение

Несмотря на то что Perceived Performance очень субъективная метрика, ее можно и нужно измерять. Об этом говорилось в докладе Виктора Русаковича на FrontendConf 2019.

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

Сделать это можно при помощи сервиса под названием Яндекс.Толока.

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

Даже если в конце концов вашему начальнику покажется, что быстрее не стало, проведите исследование и попробуйте улучшить ситуацию с помощью Perceived Performance.

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

Конференция, посвященная всем аспектам разработки клиентской части веб проектов, FrontendConf 2021 пройдет 29 и 30 апреля. Билеты можно приобрести здесь. Вы можете успеть купить их до повышения цены!

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

Черновик

Подробнее..

Материалы митапа для андроид-инженеров поиск проблем сборки, защита от них и работа с Gradle

16.03.2021 18:09:12 | Автор: admin

Недавно прошёл наш Android meetup, где ребята из платформенной команды Авито делились своим опытом работы с Gradle, показывали способы защиты от частых проблем при сборке проектов и рассказывали о нашем подходе к решению проблем.

Собрали в посте видеозаписи выступлений с таймкодами и ссылки на презентации спикеров.

Gradle в 2021: сonvention plugins workshop Дмитрий Воронин

Воркшоп о способе организации проектов,который позиционируется командой Gradle как идиоматический.

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

00:00 Представление спикера и темы

05:27 Проект, который будет примером в воркшопе

06:44 Лайвкодинг: пошаговая оптимизация проекта

28:31 Ответы на вопросы

Полезные ссылки:

Lint для сборки: как защищаться от проблем при сборке проекта Евгений Кривобоков

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

00:00 Представление спикера и темы

01:38 Какие бывают проблемы

09:05 Как контролировать окружение

14:16 Пример специфической проблемы для конкретного проекта и её решения

20:04 Зачем вообще писать свои проверки

22:35 Ответы на вопросы

Посмотреть презентацию Евгения

Gradle build scan на коленке Сергей Боиштян

На боевом примере Сергей разбирает, как мы упростили поиск ошибок в своих CI-сборках. Вы узнаете, как мы применяем продуктовый подход в решении проблем и немного о том, как работаем с Gradle.

Доклад будет полезен тимлидам в больших командах, разработчикам, которые настраивают CI/CD и разработчикам, которые решают любого рода проблемы.

00:00 Представление спикера, темы и её пользы

04:12 Поиск проблемы: разбираем на примере падения сборки

06:51 Определяем приоритет задач по RICE

14:36 Как мы искали решение проблемы

18:30 Пишем прототип с помощью TestProjectGenerator

26:11 Версия инструмента 1.0

30:30 Отдаём инструмент пользователям и смотрим на результат

34:02 Сравнение: как было и как стало

36:52 Ответы на вопросы

Посмотреть презентацию Сергея

На этом всё, до встречи на новых митапах!

Подробнее..

Ну, покати! или CICD мобильных приложений на основе контракта

02.09.2020 18:14:51 | Автор: admin

Всем привет! Меня зовут Дмитрий, я релиз-инженер вкоманде CI/CD Speed Авито. Вот уже несколько лет мы сколлегами отвечаем за всё, что связано срелизами наших мобильных приложений и не только. Пронаши релизные поезда и как мы кэтому шли уже очень подробно рассказывал Алексей Шпирко.


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



Немного контекста


Мобильное приложение Авито это:


  • Десятки продуктовых команд.
  • 20+ разработчиков на каждую изплатформ.
  • Тысячи UI-тестов.
  • Десятки тысяч UNIT-тестов.
  • Сотни тысяч строк кода.
  • Еженедельные релизы Android.
  • Релизы iOS раз вдвенедели.

Процесс релиза состоит изследующий частей:


  1. Срез релизной ветки изdevelop и простановка тега вgit.
  2. Прогон всех автоматических проверок кода и прогон всех видов тестов.
  3. Сборка релиз-кандидата.
  4. Загрузка релиз-кандидата вAppStore/GooglePlay и внутреннее хранилище артефактов.
  5. Отправка необходимой информации всистемы мониторинга.
  6. Загрузка данных всистему управления фича-тоглами.
  7. Сборка what's new дляQA и редакторов.
  8. Подготовка Jira-артефактов простановка версии в задачи, создание задач дляредакторов, QA и релиз-инженеров.
  9. Нотификация всех заинтересованных лиц оготовности релиз-кандидата.
  10. Регрессионное тестирование.
  11. Выпуск приложения начасть пользователей и нотификация обэтом.
  12. Выпуск приложения на100% пользователей и снова нотификация.


Так устроен наш релизный поезд


В начале 2019года всё это успешно обслуживалось несколькими десяткам скриптов наразных языках и сложными цепочками TeamCity-билдов. Каждое воскресенье cron запускал стартовую TeamCity-конфигурацию, скрипты и билды выполняли всю работу изпунктов 1-9.


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


Но вкажущемся благополучии таился ряд проблем.


Проблема 1. Сложные цепочки билдов вTeamCity


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



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


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

Например билд-1 завершает работу, билд-3 ждёт результаты выполнения билдов 1 и 4, а вбилде-7 поменяли параметр, и вся система черезчас работы рухнула. Садишься, разбираешься что куда нужно пробросить и кому что скормить или перезапускаешь весь процесс снуля. В итоге теряешь много сил и времени и получаешь временами автоматизацию наручном приводе.


Это усугублялось другой проблемой или же особенностью организации нашей работы.


Проблема 2. Зоны ответственности


Так сложилось, что наша большая команда состоит издвух независимых команд поменьше. Это собственно мы CI/CD team и наши коллеги Testing team. Мы отвечаем завсю общую часть релиза или же CD как взять нужный срез кода и донести его пользователям. Ребята изTesting team отвечают завсю платформа-специфичную часть как собрать приложение, прогнать нанём нужные тесты и отдать это нам.


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


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


Проблема 3. Люди


У нас есть часть релизного процесса, вкоторой участвуют люди. Это непосредственные участники: тестировщики, редакторы, релиз-инженеры. И косвенные, но заинтересованные втом, чтобы пользователи получили приложение: продакт-менеджеры, разработчики, маркетологи, аналитики. Раньше вся коммуникация осуществлялась через Slack-каналы, а актуальное состояние релиза было разбросано поразным местам (Jira, Slack), его знал только релиз-инженер. Поэтому ему приходилось тратить много времени отвечая навопросы когда поедем на 100%?, релиз стартанул?, так уже можно тестировать или нет?, а следующий релиз когда?.


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


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


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


Давайте посмотрим, что мы вкладываем вэти понятия.


CD:


  • срез релизной ветки вgit;
  • простановка тегов вgit;
  • запуск CI-части;
  • подготовка релизных артефактов (Jira-задачи, Release Notes);
  • подготовка регрессионных артефактов;
  • оповещения остадиях релиза;
  • релиз.

CI:


  • прогон всех тестов;
  • сборка приложения;
  • сборка платформ-специфических артефактов;
  • загрузка приложения вмаркет.

Видно, что есть разграничение зон ответственности напроцессном уровне и есть четкое разграничение наорганизационном уровне. Но вобщем релизном процессе науровне TeamCity всё перемешивалось и доставляло очень много проблем.


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


Контракт по своей сути это пара JSON-файлов, один изкоторых CD передаёт вCI-часть, а второй ожидается как результат работы CI.



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


Пример входного файла контракта config.json:


{"schema_version": 1, "project": "avito", "release_version": "777.5", "output_descriptor": {        "path":"http://artifactory.ru/releases/avito_android/777.5_1/output.json",         "skip_upload": false}, "deployments":  [        {        "type": "google-play",        "artifact_type": "bundle",        "build_variant": "release",         "track": "beta"        }  ]}

Тут мы сообщаем CI-части, что хотим собрать релиз проекта Авито сномером 777.5, ожидаем, что выходной файл врезультате работы будет загружен попути, описанному вoutput_descriptor, а также заказываем, какие артефакты и вкаком виде должны быть собраны и куда загружены после.


Пример выходного файла контракта output.json:


{  "schema_version": 1,  "teamcity_build_url": "https://tmct.ru/viewLog.html?buildId=17317583",  "build_number": "777",  "release_version": "777.5",  "git_branch": {    "name": "release-avito/777.5",    "commit_hash": "2c54c50c220bf91bc1a6ca10b34f53a540c80551"  },  "test_results": {    "report_id": "5f3e94fd23d67bf434e5c1b8",    "report_url": "https://tests.avito.ru/report/AvitoAndroid/FunctionalTests/2c54c50c220bf91",    "report_coordinates": {      "plan_slug": "AvitoAndroid",      "job_slug": "FunctionalTests",      "run_id": "2c54c50c220bf91"    }  },  "artifacts": [    {      "type": "apk",      "name": "avito-777.5-777-release.apk",      "uri": "http://example.com/artifactory/android/avito/777.5-777/avito-777.5-777-release.apk",      "build_variant": "release"    },   ]}

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


Nupokati: сервис релизов мобильных приложений


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


Поэтому мы решили отказаться отTeamCity вCD и реализовывать собственный сервис релизов мобильных приложений.


Что мы хотели получить отнового сервиса?


  1. Отсутствие сложных связей и неявных зависимостей.
  2. Перезапуск релиза, начиная сточки отказа.
  3. Прозрачность процесса релизов длявсех участников.
  4. Простую поддержку, кастомизацию и тестирование.
  5. Возможность использования наразных мобильных проектах компании.

Так появился сервис мобильных релизов Nupokati рабочее название прижилось и осталось снами.



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


Основная управляющая сущность вCD-сервисе это Release.



Он, как конструктор, собирается из различных шагов:



Вот пример небольшой части релиза:



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


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




Здесь есть вся необходимая информация порелизу и ссылки наартефакты



Также отсюда осуществляется всё управление релизом



И отображается актуальное положение релизного поезда



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


Итоги


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


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

Подробнее..

Crash-crash, baby. Автоматический мониторинг фатальных ошибок мобильных приложений

15.09.2020 12:21:41 | Автор: admin

Всем привет! Меня зовут Дмитрий, я релиз-инженер вкоманде CI/CD Speed Авито. Вот уже несколько лет мы сколлегами отвечаем за всё, что связано срелизами наших мобильных приложений и не только. Впрошлый раз я рассказывал онашей системе релизов мобильных приложений наоснове контракта. Сегодня речь пойдет отом, как мы автоматизировали сбор информации изFirebase оновых фатальных ошибках вмобильных приложениях.



Проблематика


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


Раньше, как и многие нарынке мобильных приложений, мы использовали Fabric, длякоторого vadimsmal и YourDestiny написали очень удобный клиент Fabricio. Набазе этого клиента унас была создана система мониторинга, которая заводила Jira-задачи нановые фатальные ошибки, искала ответственных поGit-Blame и сообщала обошибках вcпециальный слак-канал.


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


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


Получаем данные


Google Cloud Functions


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


Исследование документации Firebase привело нас кGoogle Cloud Functions или же облачным функциям. Это serverless FaaS отGoogle, который позволяет запускать ваш код воблачной инфраструктуре Google. УFirebase-Crashlytics есть встроенная интеграция соблачными функциями (намомент написания статьи данная функциональность помечена как deprecated). Вы можете написать call-back наодин изтрёх crashlytics-ивентов и дальше обрабатывать его как вашей душе угодно. Особенно нас интересуют два ивента onNew(новое событие crashlytics) и onVelocityAlert (резкий рост события crashlytics).



Вголове сразу же родилась схема. Настраиваем интеграцию Firebase-Google Cloud Functions, шлём оттуда все новые краши сразу всвой сервис, и там уже обрабатываем. Берём пример издокументации, вносим несколько доработок и получаем следующий код наJS который загружаем вGoogle Cloud:


const functions = require('firebase-functions');const rp = require('request-promise');function sendEvent(event) {    return rp({        method: 'POST',        uri: functions.config().crashlytics.crash_collector_url,        body: event,        json: true,    });}exports.NewIssueEvent = functions.crashlytics.issue().onNew(async (issue) => {    await processEvent(issue, 'NewIssueEvent')});exports.RegressedEvent = functions.crashlytics.issue().onRegressed(async (issue) => {await processEvent(issue, 'RegressedEvent')});exports.VelocityAlertEvent = functions.crashlytics.issue().onVelocityAlert(async (issue) => {await processEvent(issue, 'VelocityAlertEvent')});const processEvent = async (event, type) =>{    if (isActualEvent(event)) {        await sendEvent(event);        console.log(`Posted ${type} ${event.issueId} successfully to crash collector`);    }    else{        console.log(`It's old event or not Avito. Do nothing`);    }}const isActualEvent = (event) =>{    const {appInfo} = event;    const {appName, latestAppVersion} = appInfo;    const version = latestAppVersion &&  parseFloat(latestAppVersion.split(' ')[0]);    console.log(`Event appName: ${appName} version: ${version}`);    return appName === 'Avito' && version > 60.0}

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


Но втекущей реализации нам не хватает данных. ВFirebase-Crashlytics есть fatal события (собственно фатальные ошибки-краши) и non-fatal (остальные события которые по той или иной причине логируются в crashlytics). Все летящие кнам ивенты насобытие onNew не имеют признака фатальности, ктому же нам хотелось как-то фильтровать события поколичеству затронутых пользователей и частоте возникновения, но этой информации всобытиях нет.


BigQuery


Google позволяет экспортировать данные изFirebase вBigQuery. BigQuery облачное хранилище, предоставляющее удобную платформу дляхранения и обработки данных. На момент исследования всередине 2019года был доступен только один тип синхронизации cFirebase Batch Table.


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


  1. Синхронизация происходит раз всутки, приэтом нет гарантии, когда она будет завершена.
  2. Нельзя настроить тип экспортируемых событий экспортируется и fatal и non-fatal.
  3. Чем дольше живёт таблица, тем больше вней данных (ваш кэп) и тем дороже стоят услуги хранения.

Дорабатываем изначальную схему:



После получения ивента внашем сервисе идём вBigQuery и получаем недостающую информацию: признак фатальности, число задетых пользователей и так далее. При этом запросы кBigQuery отправляем не накаждый новый ивент, а периодически. Длянас оптимальная частота запросов раз вдень после 17:00, так как заэто время выгрузка данных изFirebase-Crashlytics вBigQuery успевала завершиться, и можно было получить информацию повсем необработанным ивентам простым запросом:


SELECT issue_id, is_fatal, COUNT(*) as crashes_counter, COUNT(DISTINCT installation_uuid) AS affected_users FROM `android.firebase_crashlytics.{table}` WHERE issue_id in ( {issues_id_string} ) GROUP BY issue_id, is_fatal LIMIT 1000

Внимательный читатель может заметить, что тут образовывается временной лаг между фактическим появлением краша и получением нами информации онём. Чтобы не пропускать редкие, но действительно важные краши, которые резко растут и задевают сразу много пользователей, унас по-прежнему оставалось событие onVelocityAlert вGoogle Cloud Function. Подокументации это событие вызывается исключительно нафатальные ошибки вработе приложения, если ошибка привела ксбою N сеансов пользователей запоследний час. Пофакту же onVelocityAlert не работало, мы зарепортили это вGoogle, нас внесли вовнутренний трекер, и наэтом всё.


Слак


Нас вполне устраивал временной лаг приполучении обычных фатальных ошибок, но совсем не хотелось пропустить пожар. Впоисках возможности получения VelocityAlert мы пришли кинтеграции Firebase-Crashlytics и слака. Вконсоли Firebase вы можете достаточно просто и быстро настроить отправку событий вопределённый слак-канал, втом числе событий VelocityAlert. Это было почти то, что мы искали. Интеграция позволяла оперативно получать информацию отом, что всё плохо, но приэтом нельзя было фильтровать события поверсии. Витоге канал больше напоминал свалку.


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


Новая схема выглядела так:



Обрабатываем данные


Систочником данных вроде определились. Теперь нужно эти данные обрабатывать.


Напомню, что наша старая система наFabric делала сданными окрашах:


  1. Искала ответственного поGit-Blame.
  2. Создавала задачу наисправление.
  3. Оповещала оновом событии в специальный слак-канал.

Первое отчего мы решили отказаться это автоматическое создание задачи и поиск ответственного поGit-Blame. Поопыту, автоматически созданные задачи отправлялись накладбище Jira, и кним редко кто возвращался, а поиск поGit-Blame иногда давал сбой, что ещё больше повышало шансы забыть задачу. А вот оповещения вслак мы решили развивать, этот канал коммуникации показал себя наиболее эффективным.


Обработку решили реализовать набазе сервиса мобильных релизов Nupokati. Он собирает информацию поновым крашам, раз вдень покрону запрашивает дополнительные данные изBigQuery, фильтрует краши пофатальности и частоте возникновения нас не интересуют единичные сбои и отправляет daily report вслак поактуальной версии приложения.



Пример daily report


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


Помимо daily report мы отлавливаем VelocityAlert дляактуальной версии и тут же репортим опожаре вслак-канал и ответственному законкретный релиз инженеру. Втреде определяется, насколько взрыв фатален, и что сним делать.



Google Cloud Functions всё


Около года мы успешно эксплуатировали новую систему автоматического сбора и алертинга фатальных ошибок вмобильных приложениях. Уже практически забыли, как заходить вFirebase и смотреть краши. Как вдруг было объявлено, что интеграция Firebase-crashlytics и Google Cloud Functions deprecated и её работа будет приостановлена 1октября 2020года. Нужно было оперативно дорабатывать решение и отказываться отоблачных функций. Приэтом хотелось обойтись минимальными изменениями вработающей системе.



Так мы просто убрали Cloud Functions и доработали запрос наполучения данных изBigQuery. Вся остальная система осталась прежней: daily report, velocityAlerts, фильтры поколичеству задетых пользователей и слак-каналы. Новый запрос получает сразу все уникальные краши понужной версии и отправляет их впоток обработки.


SELECT issue_id, issue_title, is_fatal, COUNT(issue_id) as crashes_counter, ARRAY_AGG (distinct application.display_version) AS versions, COUNT(DISTINCT installation_uuid) AS affected_users FROM `android.firebase_crashlytics.{table}`WHERE is_fatal=true GROUP BY issue_title, issue_id, is_fatal HAVING ARRAY_LENGTH(versions)=1 AND "{version}" in UNNEST(versions)ORDER BY crashes_counter DESC

Итоги


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


Несколько советов тем, кто захочет повторить наш путь:


  • Использование BigQuery платное, но есть песочница, вкоторой можно поэкспериментировать.
  • Оптимизируйте запросы кBigQuery. Процессинг данных не бесплатный, он впрямом смысле имеет денежное выражение согласно тарифам.
  • Дляоптимизации затрат нахранение данных вBigQuery уменьшайте время жизни таблиц, это есть внастройках. Длянас оптимальным отказался период жизни таблицы впять дней.
  • Уже после создания нашей системы появился BigQuery streaming. Нанём можно собрать аналогичную систему или даже лучше.
  • Внимательней читайте документацию кGoogle Cloud Platform. Это очень мощная платформа смножеством инструментов и возможностей.
Подробнее..

Avito Android meetup работа с Gradle и проблемы при сборке проектов

01.03.2021 12:17:20 | Автор: admin

Привет, Хабр! 11 марта в 18:00 по Москве мы проведём онлайн-митап для андроид-разработчиков.

В этот раз без внешних спикеров все доклады будут от инженеров нашей платформенной команды Speed, которые отвечают за быструю доставку изменений во всех андроид-приложениях Авито до пользователей. Ребята каждый день решают задачи, связанные с CI/CD и локальной работой с проектами, так что им есть, чем поделиться.

Доклады

Как правильно писать на Gradle в 2021 Дмитрий Воронин

В Gradle есть куча способов решить одну и ту же задачу, однако часть из них оптимальнее других. Я разберу несколько распространённых ошибок и покажу, как сделать лучше.

Как защищаться от частых проблем при сборке проекта Евгений Кривобоков

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

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

Gradle build scan на коленке Сергей Боиштян

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

Доклад будет интересен тем, кто страдает от поиска ошибок в своём CI и developer experience инженерам, которые могут переиспользовать наше решение или идеи.

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

Пароли и явки

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

Ещё вы можете добавить событие себе в календарь вот ссылка на событие для ICS-календаря, который обычно работает на айфонах и маках, а вот для Google-календаря. Если хочется получить напоминание со ссылкой на эфир на электронную почту, можно зарегистрироваться на таймпаде. Запись тоже будет опубликуем ссылку на неё в отдельном посте.

До встречи в онлайне!

Подробнее..

Как написать симпатичный чейнджлог опыт Авито

25.05.2021 12:05:50 | Автор: admin

Привет! Меня зовут Гера, я продуктовый редактор вАвито. Пишу тексты для интерфейсов ирассылок, аещё чейнджлоги дляGooglePlay иAppStore. Это тексты, вкоторых рассказывается, что появилось вновой версии приложения. По-английски их ещё иногда называют what'snew или releasenotes.

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

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

А зачем оно всё

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

Есть ли какая-то логика вприменении шаблонов неизвестноЕсть ли какая-то логика вприменении шаблонов неизвестно

Сторы включают пользователям автоматическое обновление: многие и не знают, что загрузили новую версию приложения. App Store идёт дальше: раньше чейнджлоги жили вотдельной вкладке наглавном экране, но в2019году скрылись внастройках. Наиконке был счётчик необновлённых приложений, но и он пропал. Ктому же, оба стора не дают узнать, сколько людей прочитали текст.

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

Читать такие сообщения приятно. Особенно учитывая, что это пустышка новых фич врелизе не былоЧитать такие сообщения приятно. Особенно учитывая, что это пустышка новых фич врелизе не было

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

Процесс в Авито

Сжатые сроки

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

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

Неуловимые изменения

Вместе с задачей начейнджлог редактор получает таблицу сизменениями врелизе.

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

Типичные названия задач изтаблицы: какие-то иностранные колонки, богатые парсинги и чистые симуляторыТипичные названия задач изтаблицы: какие-то иностранные колонки, богатые парсинги и чистые симуляторы

Есть и ещё одна сложность. Почти все фичи сначала тестируются начасти пользователей всреднем наАвито одновременно крутятся 80экспериментов. Даже если изменение решают катить, отследить этот момент непросто. Фичу могут включить безрелиза набэкенде, тогда отдельной задачи и не появится.

Кто виноват? Что делать?

Детективная работа

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

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

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

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

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

Формальности

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

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

И так подва раза каждую неделю. Не считая майских и новогодних каникул.

Советы по написанию

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

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

На месте автора я бы задался вопросом: какая ценность в изменениях? Может, оптимизация ускорила загрузку страниц? Или новые цвета сделали приложение удобнее для людей с плохим зрением? Если ценности нет, то можно инерассказывать об измененияхНа месте автора я бы задался вопросом: какая ценность в изменениях? Может, оптимизация ускорила загрузку страниц? Или новые цвета сделали приложение удобнее для людей с плохим зрением? Если ценности нет, то можно инерассказывать об изменениях

Соблюдайте законы сторов

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

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

Так выглядят примерно 99,9% чейнджлоговТак выглядят примерно 99,9% чейнджлогов

Также помните олимитах. Это актуально скорее дляGooglePlay: внём ограничение 500символов. ВAppStore 4000, нужно ещё умудриться столько написать: это четверть этой статьи.

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

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

Помните, очём икак уже писали

Человек видит чейнджлоги примерно водном итомже месте авAppStore даже может полистатьисторию версий. Поэтому начинайте иструктурируйте текст по-разному всоседних релизах.

У нас есть файлик с историей чейнджлоговУ нас есть файлик с историей чейнджлогов

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

Непишите отестах

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

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

Рассказывайте омасштабных изменениях

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

Речь о фиче, которой могут воспользоваться практически все пользователи Авито, которые начнут создавать объявление. Не рассказать о таком было бы преступлениемРечь о фиче, которой могут воспользоваться практически все пользователи Авито, которые начнут создавать объявление. Не рассказать о таком было бы преступлениемКонечно, Яндекс.Картами пользуются не только вМоскве, но москвичей много и обновление дляних значимоеКонечно, Яндекс.Картами пользуются не только вМоскве, но москвичей много и обновление дляних значимое

Иллюстрируйте пользу

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

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

Аккуратно говорите обошибках искорости работы

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

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

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

Если шутите, то без кринжа

В Авито мы некасаемся политики идругих острых тем. Если шутка рождается сама походу написания, проверяемеё накринжовость, оскорбительность ипонятность.

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

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

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

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

Однажды у нас вышел милый чейнджлог опопулярной породе собак:

Но пользователей Авито так много, что кто-то всё равно непонял:

Непревращайтесь вроботов

Хотьипишем отехнических штуках, важно оставаться человеками.

В принципе тут чувствуешь, что с тобой говорит РЖД, а не человек В принципе тут чувствуешь, что с тобой говорит РЖД, а не человек

Что делать, если писать неочем

В Авито в 2020году на Android вышло 46релизов, и 16 то есть треть были счейнджлогами-пустышками. На то есть причины: иногда редактору не удаётся найти интересную тему, а иногда их действительно нет.

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

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

Или об интересном исследовании:

Можно написать что-то ситуативное например, привязаться кчемпионату мира пофутболу:

Или рассказать очём-то, что можно найти наАвито:

Если соблюсти формальности и сказать обисправлениях, можно аккуратно прорекламировать икакой-то свой проект:

Кратко: как писать симпатичные чейнджлоги

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

  • Говорите, что изменилось. Вряд ли вам нужно, чтобы релиз отклонили, особенно, если внём исправлена неприятная ошибка.

  • Не преувеличивайте. Если приложение нестало работать быстрее уабсолютного большинства пользователей, не стоит говорить, что всё внезапно начнёт летать.

  • Будьте последовательны. Чейнджлог ещё одна точка соприкосновения пользователя спродуктом. Если обычно общаетесь навы и,вообще, деловито, нестоит писать Зацени, обновление пушка!

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

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

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

Подробнее..

На чем писать Android UI-тесты

04.09.2020 12:07:35 | Автор: admin

Всем привет. Мы вAvokado Project продолжаем рассказывать проавтотестирование вAndroid. Эта статья обзор и сравнение существующих инструментов длянаписания UI-тестов.


Давайте начнем стого, что вспомним, как обычно выглядит процесс тестирования. Будем называть сущность, которая взаимодействует сприложением, клиентом. Длявзаимодействия сприложением клиенту обычно доступно несколько интерфейсов: API, REST API, CLI, GUI и т.д. И если, например, API используются клиентами-программами, тоGUI используется человеком.


Ожидания отповедения приложения описываются вспецификации. Задача тестирования проверить, что поведение приложения соответствует спецификации.



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


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


Для реализации тестов и их автоматизированного запуска нам понадобится фреймворк тестирования (JUnit, Cucumber). Он включает всебя:


  • Набор функций для проверок в тестах ассерты (assertions).
  • Механизм создания фикстур дляшаринга данных междутестами. Вобщепринятом смысле фикстура фиксированное состояние окружения, накотором выполняются тесты.
  • Раннер длязапуска тестов.

В зону ответственности раннера всвою очередь входят:


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

Несмотря на то, что сфреймворком поставляется также и раннер, существует возможность использования более совершенных сторонних раннеров. Тема раннеров, их особенностей вконтексте Android-тестирования, и выбора наиболее подходящего (Avito test runner, Marathon, Spoon, Fork) довольно обширна и заслуживает отдельной статьи, поэтому вернемся ксамим тестам.


Если API приложения доступен тесту напрямую, то дляGUI интерфейса нужны программные адаптеры драйверы.



Тесты, обращающиеся кGUI-драйверам, называются UI-тестами. Вотличие отunit-тестов, они выполняются нареальном девайсе или эмуляторе подуправлением соответствующей мобильной операционной системы.


GUI-драйверы являются наиболее сложным компонентом всего стека тестирования. Они решают базовые, низкоуровневые задачи. Когда вы даете GUI-драйверу (Espresso, UiAutomator) команду кликнуть накнопку, он обрабатывает ее, взаимодействует сприложением, и эта команда превращается вклик поэлементу.


Работа с API драйвера напрямую изтеста имеет ряд недостатков:


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

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



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


Драйверы


В Android-мире четыре популярных драйвера:


  • Espresso.
  • UiAutomator.
  • Robotium.
  • Selendroid.

Все они работают наAndroid Instrumentation Framework базовом API Android длявзаимодействия ссистемой. Самые популярные Espresso и UiAutomator. Они оба разрабатываются и поддерживаются компанией Google. Их без труда можно использовать одновременно впределах одного теста. Давайте рассмотрим их поближе.


UiAutomator



UiAutomator поставляется вместе сAndroid SDK начиная с16 версии API. Как GUI-драйвер он служит дляпоиска элементов интерфейса наэкране девайса и эмуляции различных действий: кликов, тачей, свайпов, ввода текста, проверок на видимость. Рекордер длязаписи тестов он не предоставляет, зато предоставляет утилиту дляпросмотра древовидной структуры экрана Ui Automator Viewer.


UiAutomator позволяет писать тесты помодели черного ящика (black-box). Он живет всобственном процессе и работает бездоступа кисходному коду приложения. Значит, тест сего использованием может взаимодействовать практически слюбыми приложениями, втом числе системными. Это открывает доступ кмножеству функций, например, становятся возможны:


  • набор номера и совершение звонка;
  • отправка и чтение смс;
  • взаимодействие с нотификациями;
  • управление настройками сети, геолокации;
  • снятие скриншотов.

Внутри UiAutomator реализован припомощи AccessibilityService, облегчающего работу сдевайсом людям сограниченными возможностями. Именно AccessibilityService знает, что вкаждый конкретный момент времени отрисовано наэкране и умеет сэтими элементами взаимодействовать. Дляэтого он строит виртуальное дерево компонентов просматриваемого экрана, вузлах которого находятся объекты AccessibilityNodeInfo. Эти узлы содержат метаинформацию осоответствующих им View: имя класса, координаты или отображаемый текст.



Каждая View реализует интерфейс AccessibilityEventSource и через Binder IPC оповещает системный AccessibilityManagerService окаждом действии, произведенном сней, например, оклике или смене фокуса. AccessibilityManagerService передает эти данные вкаждый активный наданный момент AccessibilityService и, вслучае теста, UiAutomator, которые обновляют свое виртуальное дерево компонентов. Потакой же схеме вобратную сторону передаются команды отAccessibilityServiceов и UiAutomatora котображаемым элементам интерфейса любого приложения всистеме. Если элемент интерфейса не является наследником View, то дляучастия втакой схеме длянего потребуется самостоятельно реализовать ряд методов.


Для более детального погружения вустройство работы UiAutomator рекомендуем ознакомиться сдокладом UI Automator deep diving.


Среди недостатков UiAutomator:


  • Сложный и универсальный механизм работы, затрагивающий Binder IPC, реализован неидеально. Длятого, чтобы выполнить то или иное действие UiAutomator ждет, пока приложение придет вконсистентное состояние. Он ожидает появления временного окна, втечение которого отсистемы не поступит никаких событий. Такое устройство приводит не только кнестабильности и низкой скорости исполнения команд, но часто даже кдлительным задержкам или кполной остановке теста.
  • Элементы интерфейса, не являющиеся наследниками View или отрисованные припомощи OpenGL или Unity, невидимы дляUiAutomatora, если разработчики заранее обэтом не побеспокоились. Поэтому, например, привзаимодействии смобильными играми уUiAutomatora могут возникать проблемы.

Таким образом, наиболее подходящим сценарием дляиспользования UiAutomator является не работа стестируемым продуктовым приложением, а взаимодействие состоронними или системными приложениями. Более подходящим инструментом дляработы спродуктовым приложением является Espresso.


Espresso



Это также официальный фреймворк дляUI-тестирования отGoogle, но тесты сего использованием работают уже помодели белого ящика (white-box). Espresso поддерживает Android API с10версии, хотя появился значительно позже. Предоставляет рекордер длязаписи тестов.


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


Espresso решает низкоуровневую задачу найти необходимый элемент наэкране позаданным параметрам (ViewMatcher), произвести сним действия (ViewAction) или выполнить проверки (ViewAssertion).



Синтаксис Espresso реализован наоснове фреймворка Hamcrest. Он построен наиспользовании иерархий вложенных матчеров сущностей, описывающих требования кобъекту, вслучае Espresso кэлементам интерфейса. Они используются призадании параметров поиска элемента наэкране, а также впроверках как описание свойств, которыми элемент должен обладать. Вложенность матчеров часто приводит ктому, что код тестов становится трудно читать и поддерживать.


@Testpublic void espressoTest() {    onView(allOf(allOf(withId(R.id.label_bf_hotelname),         isDescendantOfA(withId(R.id.custom_view_trip_review))),         isDescendantOfA(withId(R.id.contentView))))        .check(matches(            withEffectiveVisibility(Visibility.VISIBLE)        ));}

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



Для того, чтобы убедиться вэтом, достаточно обратиться квнутренностям, например, метода check класса ViewInteraction. Передаваемый аргументом объект ViewAssertion вызовется наглавном потоке после ожидания idleа.


public ViewInteraction check(ViewAssertion viewAssertion) {    // (...)    runSynchronouslyOnUiThread(new Callable<Void>() {        @Override        public Void call() {            uiController.loopMainThreadUntilIdle();            // (...)            viewAssertion.check(...)        }    });}

Исходя извышесказанного можно выделить следующие недостатки Espresso:


  • Ему требуется доступ кисходному коду тестируемого приложения.
  • Он не видит ничего запределами процесса тестируемого приложения и не может взаимодействовать сдругими приложениями. Например, совершить звонок удастся, только если тестируемое приложение и есть звонилка. А вот кликнуть нанотификацию Espresso уже не сможет совсем. Скорее всего втестах придется прибегать кпомощи UiAutomator.
  • API Espresso построен таким образом, что не способствует написанию чистого и читаемого кода тестов. Обэтом придется придется заботиться отдельно.
  • У Espresso проблемы состабильностью приработе сосложными асинхронными интерфейсами и сосписками.

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


Robotium и Selendroid


Robotium и Selendroid были довольно популярны допоявления Espresso и UiAutomator. Cейчас же их аудитория заметно сократилась, и развиваются они далеко не так активно. Оба инструмента могут работать только содним тестируемым приложением, не требуют доступа кисходному коду и поддерживают работу наэмуляторах и реальных устройствах.


Robotium может использоваться длятестирования приложений наAndroid API 8+, (поддержка работы сWebView доступна, однако, только сAPI 15), тесты длянего пишутся наJava. Также он предоставляет плагин Robotium Recorder дляIntelliJ IDEA и Android Studio.


Selendroid поддерживает ограниченный список версий API с10 по19. Он поддерживает протокол WebDriver, предоставляет утилиту Inspector дляпросмотра иерархии элементов и записи простых record-and-playback-тестов.


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


Robolectric


Несмотря нато, что Robolectric не вполне вписывается вописанную ранее структуру, его нельзя здесь не упомянуть. Он не является интеграционным тестовым фреймворком, сего помощью вы не сможете тестировать взаимодействие Android компонентов или писать UI-тесты. Однако Robolectric позволяет писать особые unit-тесты наоснове JUnit с использованием Android API.


Он мокирует часть Android SDK, предоставляя пользователю так называемые shadow-объекты. Robolectric берет насебя такие задачи, как inflate view, загрузка ресурсов, и множество других, которые имеют нативную С-реализацию наAndroid-девайсах. Поэтому Robolectric позволяет писать тесты, имеющие зависимости наAndroid, но запускать их не наэмуляторе или реальном девайсе, а надесктопной JVM. Это существенно ускоряет процесс сборки, запуска и выполнения тестов.


Обертки


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


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


Прежде, чем мы рассмотрим и сравним наиболее популярные обертки, выделим основные требования ктестам.


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


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


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


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


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


Скриншоты. Одних логов бывает недостаточно. Очень часто возникает потребность увидеть, что реально отрендерилось наэкране намомент ошибки. Поэтому было бы очень кстати, если бы клогам прилагались скриншоты.


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


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


Теперь давайте сравним популярные обертки сэтими хотелками.


Appium



Appium это довольно популярный кросс-платформенный open source инструмент дляавтоматизации тестирования десктоп и мобильных приложений под Android и iOS. Архитектура Appium схожа сSelenium WebDriver, широко распространенным и ставшим стандартом вweb-тестировании. Кроссплатформенность достигается засчет использования разных драйверов дляразных платформ. Именно драйверы транслируют клиентский Appium-код вкоманды, непосредственно исполняемые наустройствах. Длянаписания тестов Appium предоставляет клиентские библиотеки сфиксированным API.



Стабильность. Как видно изрисунка, Appium является довольно громоздкой оберткой. Посути он представляет собой целый HTTP-сервер наNode.JS, который создает и обрабатывает WebDriver-сессии, где и происходит общение сконечным драйвером команд наустройстве. Такое сложное устройство хоть и позволяет абстрагироваться отплатформы, все же негативно сказывается как наскорости, так и настабильности тестов. Ксложным и громоздким механизмам, скрытым вдрайверах, Appium добавляет собственный оверхэд.


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


Выразительный API. Длянаписания тестов Appium предоставляет клиентские библиотеки сфиксированным фасадным API, очень похожим наWebDriver. Appium не предлагает никакой архитектуры длятестов, все отдается наоткуп конечному разработчику.


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


Логи. Не предоставляет инструмента длялогирования шагов, действий и проверок теста.


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


Доступ к adb. Благодаря своей клиент-серверной архитектуре уAppiumа нет проблем стем, чтобы посылать надевайс adb-команды. Это, бесспорно, является его плюсом.


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


Также стоит отметить, что Appium это отдельная и зачастую чуждая Android-разработчикам технология. К тому же, если ввашем проекте используется Kotlin, втестах придется вернуться кJava. Из-заэтой чуждости Appium часто встречает неприятие состороны разработчиков. Это является негативным фактором, поскольку понашему опыту как раз разработчики должны драйвить процесс автотестирования. Подробнее вэтих докладах: Автотесты вАвито. Зачем они, как помогают, сколько стоят, Как начать писать автотесты и не сойти сума.


Kakao



Kakao простой и удобный Kotlin DSL дляEspresso. Он позволяет упростить код тестов наEspresso и повысить его читаемость.


Выразительный API. Посути Kakao это написанный завас boilerplate-код дляподдержки втестах следующих двух паттернов:


  • KView это Kakao-представление элемента интерфейса наэкране, скоторым происходит взаимодействие вовремя теста. Например, Kakao предоставляет такие готовые реализации, как KEditText, KButton и т.д. Пользователь может добавлять собственные.
  • Screen это реализация пришедшего измира веб-разработки паттерна PageObject. Screen это базовый класс, наоснове которого рекомендуется создавать собственные stateless-хранилища всех KView насоответствующих экранах приложения. Рекомендуется создавать отдельный Screen длякаждой активити, фрагмента или, например, диалога вашего приложения. Screenы описываются вотдельных файлах, таким образом работа пообнаружению элементов интерфейса наэкране отделяется отсамих тестов.

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


@Testpublic void espressoTest() {    onView(allOf(allOf(withId(R.id.label_bf_hotelname),        isDescendantOfA(withId(R.id.custom_view_trip_review))),        isDescendantOfA(withId(R.id.contentView))))        .check(matches(            withEffectiveVisibility(Visibility.VISIBLE)        ));}

получаем это:


object MainScreen : Screen<MainScreen>() {    val hotelName = KTextView { withId(R.id.label_bf_hotelname) }}@Testfun kakaoTest() {    MainScreen {         hotelName {             isVisible()         }     }}

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


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


Гибкость. Дляобеспечения расширяемости вKakao реализован механизм интерсепторов. Пользователь черезспециальное API может внедрять вызов собственного кода вмомент, когда выполняется та или иная Espresso-команда. Благодаря этому пользователь может самостоятельно добавить втесты, например, ожидания или повторы неудачных действий. Однако что-либо сделать вне процесса тестируемого приложения по-прежнему не удастся.


Логи. Логов изкоробки нет, как и вEspresso. Хотя можно добавить их самостоятельно припомощи интерсепторов.


Скриншоты. Механизма снятия скриншотов изкоробки также нет. Потребуется собственная реализация.


Доступ к adb. Отсутствует, как и уEspresso.


Итог. Таким образом, Kakao это удобный DSL дляупрощения написания и поддержки тестов наEspresso. Но помимо него длярешения многих бытовых задач вам необходимо будет использовать UiAutomator, чей API уже не такой выразительный. Скорее всего современем вам придется дописать множество собственных надстроек длярасширения функциональности, например, длялогирования и повторов.


Barista



Barista это объемная библиотека, содержащая большое количество полезных решений и приемов приработе сEspresso.


Выразительный API. Как и Kakao, эта библиотека построена поверх Espresso. Однако Barista скорее широкий набор приемов, которые можно выборочно использовать всвоих Espresso-тестах. Этот набор приемов включает всебя:


  • Удобные и лаконичные helper-методы длявзаимодействия сэлементами интерфейса, которые скрывают отнас Espresso вызовы. Например, дляобыкновенного клика покнопке вместо onView(withId(R.id.button)).perform(click()) наголом Espresso получаем простой метод clickOn(R.id.button). Или вместо множества строчек кода дляклика наэлемент списка получаем просто clickListItem(R.id.list, 4).
  • Расширенный Android-специфичный Assertions API.
  • Инструмент длямокирования результатов интентов, правда пока только для работы скамерой.
  • Инструмент дляработы сruntime permissionами.
  • Огромное количество test ruleов, например, дляперезапуска flaky-тестов или дляочистки послепрогона теста shared preferences, базы данных, или удаления файлов сустройства.

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


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


Универсальность. Исключительно белый ящик из-заработы только поверх Espresso.


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


Логи. Механизм для логирования шагов, действий и проверок втестах отсутствует.


Скриншоты. Механизм снятия скриншотов также отсутствует.


Доступ к adb. Отсутствует, как и уEspresso.


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


Kaspresso



Фреймворк-обертка дляUI-тестирования, который претендует нато, чтобы избавить мир отзла и стать практически единственной зависимостью ввашем проекте дляработы стестами.


Выразительный API. Создатели вдохновлялись наработками инженеров изАвито и красотой Kakao. Kaspresso расширяет лаконичный Kakao DSL он предоставляет собственный Kotlin DSL дляописания тестовых секций и шагов, внутри которых продолжает жить Kakao сосвоими Screenами и KView.


@RunWith(AndroidJUnit4::class)class OpenHomeScreenTest : TestCase() {    @Test    fun kaspressoTest() {        before { ... }        .after { ... }        .run {            step("1. Open Home screen") {                 MainScreen {                    openHomeScreenBtn.click()                }            }            step("2. Check Home title") {                 HomeScreen {                    title.isVisible()                }            }            step("3. Do some stuff") { ... }        }    }}

За Kaspresso API скрывается много работы длярешения основных задач, рано или поздно возникающих передразработчиком автотестов. Правильное использование фреймворка подталкивает пользователя киспользованию зарекомендовавших себя паттернов построения декларативных, поддерживаемых и стабильных тестов (см. How to write autotests).


Универсальность. Kaspresso как фреймворк-обертка агрегирует подсвоим API обращение и кEspresso и кUiAutomator. Таким образом, тесты сего использованием могут работать как внутри процесса тестируемого приложения, так и вне его, когда это необходимо. Например, если вовремя теста вам необходимо зайти встороннее приложение или кликнуть нанотификацию, сего помощью сделать это не составит проблемы. Kaspresso предоставляет Kautomator собственную Kakao-like обертку для работы сUiAutomator, что делает дляпользователя обращение кEspresso и кUiAutomator визуально неотличимым.


object KakaoScreen : Screen<KakaoScreen>() {    val title = KTextView { withText(R.string.title1) }    val btn = KButton { withId(R.id.button1) }}object KautomatorScreen : UiScreen<KautomatorScreen>() {    val title = UiTextView { withText(R.string.title2) }    val btn = UiButton { withId(pkgName, R.id.button2) }}@Testfun kaspressoTest() {    KakaoScreen {         title.isVisible()        btn.click()    }    KautomatorScreen {        title.isVisible()        btn.click()    }}

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


  • набор номера и совершение звонка;
  • отправка и чтение смс;
  • работа сфайловой системой;
  • управление настройками сети, языка, геолокации;
  • установка и удаление приложений вовремя теста;
  • снятие скриншотов;
  • смена ориентации девайса;
  • взаимодействие схардварными кнопками: back, home, recents.

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


Гибкость. Kaspresso предоставляет пользователю возможность конфигурации и расширения, позволяя внедрять кастомные реализации интерсепторов разных типов. Вы можете внедрять вызов собственного кода вответ наразличные события, например, накаждый вызов ViewAction или ViewAssertion, настарт, успешное окончание или падение шага вашего теста или всего теста целиком. Стоит отдельно подчеркнуть, что все параметры могут быть настроены передзапуском теста, ненужная функциональность может быть отключена, а недостающая добавлена (см. Configurator).


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


MainScreen {    flakySafely {        btn.click()    }}

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


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


device.screenshots.take("MainScreen_step_1")

Также см. Localization autotests.


Доступ к adb. Еще одной не менее важной составляющей Kaspresso является AdbServer HTTP-сервер, который умеет выполнять запрашиваемые изтеста adb-команды. Он запускается нахосте, ккоторому подключены девайсы или эмуляторы, накоторых будут запускаться тесты. Вовремя запуска теста надевайсе происходит соединение ссервером, и далее сервер может выполнять запрашиваемые изтеста adb-команды длянужного девайса. AdbServer используется дляреализации большинства функций, доступных черезфасад device. Также внутри тестов вам доступно простое и удобное API длявызова необходимых adb-команд:


adbServer.performAdb("emu sms send +79111111111 $smsText")adbServer.perfromShell("rm -f $filePath")

Однако AdbServer опционален, его не обязательно запускать, если вваших тестах не требуется обращение кadb.


Какую обертку выбрать


Appium Kakao Barista Kaspresso
Выразительный API + +
Универсальность +
Гибкость + +
Стабильность +
Логи +
Скриншоты +
Доступ к adb + +
Изэтой таблицы ответ должен быть очевиден. Если вы только начинаете погружение в мир автотестирования, проще всего будет начинать вместе сKaspresso. Он решит за вас множество проблем, скоторыми вы непременно столкнетесь. Он предоставляет удобный API, включающий и расширяющий Kakao, под которым скрывается обращение и кEspresso, и кUiAutomator, и кAdbServer, что значительно расширяет ваши возможности. И, что не менее важно, он содержит специальные точки расширения, так что вы сможете добиться отнего необходимого поведения.
Подробнее..

Типичные ошибки в тестовых заданиях стажёров-исследователей

20.11.2020 10:22:07 | Автор: admin

Привет, меня зовут Ксения, яисследователь вUXlab Авито. Некоторое время назад мызапустили стажёрскую программу сразу внесколько направлений: искали продуктовых дизайнеров идизайнеров коммуникаций, редакторов иисследователей.


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



Кого мыискали


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


Нужно знать, как подобрать метод подзадачу, сформулировать гипотезы исформировать выборку. Понимать, как задавать вопросы, чтобы неполучить искажённые данныеи, конечноже, как врезультате сделать корректные прикладные выводы.


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


Чтобы это случилось быстрее, мыискали вкоманду человека, вкотором сошлисьбы три основных фактора:


  1. Есть база: психологическое или социологическое образование либо релевантный опыт работы.
  2. Есть мотивация, которая понятна потестовому заданию иопыту. Мыпредполагали, что если человек действительно увлечён профессией, тоонуже пробовал применить свои навыки.
  3. Есть навыки проведения исследований.

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


Тестовое задание


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


Наше тестовое задание
Задание1.

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

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

После запуска мыхотим провести тестирование, которое ответит навопросы:
  1. Замечаютли покупатели оценки?
  2. Помогаютли они выбрать автомобиль, упрощаютли процесс поиска?
  3. Как покупатели понимают, что это заотметка, что непонятно вновой функции, какой информации нехватает?


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


Задание2.

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

Мыдобавили истории наАвито. Чтобы сделать ихмаксимально полезными, нужно исследование, которое ответит навопросы:
  1. Какие задачи пользователи решают, просматривая истории вдругих приложениях?
  2. Как люди используют истории вне социальных приложений?
  3. Начём стоит сфокусироваться при создании историй вАвито?
  4. Что важно учесть при создании этого продукта?


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


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


Неверный выбор метода исследования


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


Для решения второго задания состориз кандидаты выбирали:


  • Опрос, что слишком рано для нового продукта инеподходит под вопросы кисследованию.
  • Фокус-группу, нонеописывали разные сегменты пользователей, которых следовало нанеё позвать.
  • Интервью безюзабилити.

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


Отсутствие гипотез или неверные формулировки гипотез


Сгипотезами был целый набор проблем.


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


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


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


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


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


Вторая гипотеза впримере грешит ещё итем, что сформулирована нечётко. Изнеё непонятно, оподаче какого материала речь.


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


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


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


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


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


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


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


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


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


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


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


  • она сформулирована так, что неочевидно, как еёпроверить;
  • поскольку Авито несоциальная сеть, товрядли убизнеса будет задача сделать лучшие вмире сториз.

Некорректное формирование выборки


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


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


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


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


Ошибки всценарии исследования


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


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


Посамим вопросам всценарии были такие ошибки:


  • Закрытые вопросы. Например, замечалили выинформационные блоки санализом уровня цены иотметками хорошая цена или отличная цена?. Во-первых, натакой вопрос легко ответить просто да или нет, что малополезно вкачественном интервью. Во-вторых, онсодержит всебе подсказку, из-за которой мынеузнаем, сказалбы сам человек про эти отметки или нет.
  • Вопросы или задания втесте сподсказкой. Например, кандидаты просили найти авто сценой ниже рыночной. Такая постановка вопроса уже фокусирует внимание респондента нацене ивсём, что сней связано.
  • Сложные формулировки вопросов. Например, вопрос былили какие-либо аспекты системы, которые помогли выполнить задачи?. Для респондента исследование это беседа набытовые темы, вкоторой неместо сложным конструкциям ипрофессиональной терминологии. Если человек непоймёт ваш вопрос, тоонвлучшем случае почувствует неловкость, авхудшем непереспросит, ивынеполучите точный ответ. Намного лучше задавать простые вопросы, такие как ачто вам помогло при поиске?.

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


Чек-лист для самопроверки


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


  1. Проверьте, что вашу работу легко посмотреть: желательно, чтобы она открывалась поссылке, ненужно было ничего скачивать или где-то регистрироваться. Помните, что задания смотрят нетолькоHR, ноиспециалисты повашему треку.
  2. Подумайте отом, как оформить тестовое задание. Когда нужно проверить десятки работ, идеально, если работа каждого человека находится вотдельном файле, подписана его именем иаккуратно структурирована. Также, лучше неиспользовать для работы исследователя Фигму. Основной результат работы здесь текст. Его логичнее оформить вгугл-документе, Notion или любом другом инструменте для работы стекстом, аФигму оставить дизайнерам.
  3. Старайтесь избегать ошибок, опечаток ислишком сложных предложений. Лучше перечитать работу насвежую голову, или показать другу, чтобы убрать лишнее ипереформулировать непонятное.
  4. Стоит выполнять задания так, как указано втестовом. Если вырешили сделать по-другому, поясните, почему. Конечно, висследованиях зачастую нет единственного верного ответа, нонам важно понять, как вымыслите.
  5. Описывая выполнение задачи, постарайтесь дать всю необходимую информацию для оценки работы: параметры выборки, гипотезы, сценарий, тоесть список вопросов креспондентам, расшифровки интервью итестов, результаты ивыводы.
  6. Втоже время помните, что исследование для компании это некурсовая. Лучше опустить лишние подробности повыполнению заданий, например даты, объект ипредмет исследования. Добавляя информацию, спрашивайте себя, точноли она будет полезна или это просто следование стандартам.
  7. Продумывая гипотезы, помните, что они должны быть сформулированы чётко, однозначно ипредполагать ответ да или нет. Они должны быть именно предположениями, требующими проверки, анеочевидными утверждениями. Отформулировки гипотезы зависит выбор метода иформирование выборки, поэтому перечитайте еёдважды испросите себя: ясмогу проверить это предположение тем методом, который указан втестовом? или какой метод лучше всего поможет проверить такую гипотезу?, если это задача отзаказчика. Также убедитесь, что результат проверки гипотезы будет полезен продукту ипоможет продвинуться вего улучшении.
  8. При формировании выборки держите вголове тех людей, которые смогут лучше всего ответить наваши гипотезы, исходя изсвоего опыта ипотребностей. Кстати, когда яхотела понять, насколько хороша эта статья, япопросила еёпосмотреть людей сразным опытом изнашей команды: самого опытного эксперта, чтобы оценить верность выводов, инашего стажёра, как представителя ЦА.
  9. Когда составляете сценарий, начинайте разговор изадания среального опыта респондента.
  10. При составлении сценария фокусируйтесь наоткрытых вопросах, чтобы ненаводить человека наответы. Конкретизировать всегда успеете походу теста.
  11. Помните, что всценарии неместо сложным конструкциям ипрофессиональному жаргону. Говорите среспондентом наего языке.

Вместо заключения


Несмотря нато, что статья посвящена ошибкам, хочется отметить вконце позитивные моменты. Некоторые ребята перевыполнили задания ипроактивно сделали классные вещи. Мырассматривали как однозначный плюс:


  • Упоминание общения спродуктовой командой, которая ставит задачу.
  • Анализ конкурентов перед исследованием.
  • Кабинетный этап исследования. Например, некоторые изучили аудиторию нашего сервиса пооткрытым источникам ииспользовали это при обосновании выборки.
  • Протоколы интервью итестов.
  • Видеозаписи тестов.

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

Подробнее..

Live site review. Разбираем инциденты

26.11.2020 18:13:16 | Автор: admin

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


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


Поэтому кроме круглосуточного мониторинга унас есть процесс разбора инцидентов. Исами пожары напроде, иработы поанализу проблем мыназываем live site review или LSR. Яотвечаю зачасть работ сLSR после пожаротушения ихочу поделиться нашими наработками.



Минутка контекста


Авито большая компания. Наноябрь 2020 года унас:


  • 1134 сервиса впродакшене.
  • 58команд разработки, вкаждой изкоторых отодной дочетырёх фиче-команд поменьше.
  • 200-250 релизов изменений вдень.

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


Как устроен LSR-процесс


Работу сLSR вАвито начали в2017году. Процесс несколько раз менялся, исейчас выглядит так:


  1. Когда возникает проблема, дежурные видят еёвсистеме мониторинга.
  2. Дежурные сами чинят проблему или привлекают ответственных откоманд.
  3. Автоматика фиксирует вJira продолжительность инцидента, затронутую функциональность инедополученную прибыль. Это называется Auto LSR. Автоматикаже отмечает критичность инцидента вслучае больших финансовых потерь или большого количества жалоб отпользователей.
  4. Мызаводим постмортем тикет вJira cописанием проблемы, которая вызвала инцидент, если такого еще нет. Кнему линкуются Auto LSR.
  5. Проводим встречу скомандой иэкспертами идетально разбираем проблему.
  6. Выполняем все нужные действия попредотвращению подобных инцидентов вбудущем.
  7. Закрываем тикет идополняем базу знаний.

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


Как реагируем наинциденты


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


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



Что описываем впостмортем тикете


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


Впостмортем тикете есть фиксированный набор полей.


Summary (обязательное) это заголовок. Заголовок мыстараемся заполнять так, чтобы увидев его вкалендаре или вквартальном отчёте, можно было вспомнить, очём идёт речь.


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


Priority (обязательное) приоритет тикета. Унас ихчетыре:


  • Critical проблема из-за которой недоступен весь Авито.
  • Major проблема свысокой вероятностью повторения, недоступна часть функциональности.
  • Normal проблема сневысокой вероятностью повторения, недоступна часть функциональности.
  • Minor проблема сневысокой вероятностью повторения, недоступна часть функциональности без значимого ущерба для пользователей.

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


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


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


Участники здесь указываем всех, кого стоит позвать навстречу сразбором инцидента. Вихкалендари потом придёт приглашение.


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


Вероятность повторного возникновения ближайшие полгода экспертная оценка того, кто заполняет тикет.


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


Link from Helpdesk добавляет саппорт, если были обращения пользователей.


HDcount количество пользователей, которые обратились спроблемами поинциденту вHelpDesk. Это поле заполняется иобновляется автоматически, если заполнено поле выше.


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


Как разбираем инциденты навстречах


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


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


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


Вот список типовых вопросов, которые стоит обсудить при разборе LSR:
  • Вчём была проблема икакова еёпервопричина? Если это баг вкоде, тополезно посмотреть напроцессы тестирования ипонять почему его непоймали. Если сбой произошёл из-за возросшей нагрузки, тостоит подумать про регулярное нагрузочное тестирование.
  • Как быстро иоткуда узнали опроблеме? Можноли узнавать быстрее? Возможно, нужны дополнительные мониторинги или алерты. Или стоит отдать отдать имеющиеся команде мониторинга 24/7.
  • Как быстро смогли понять, вчём именно проблема? Возможно, стоит почистить Sentry или добавить логирование.
  • Затронулали проблема основную функциональность сайта иможноли уменьшить такое влияние? Например, если сломался счётчик количества объявлений, тостраницы сайта ломаться недолжны. Тут стоит подумать про graceful degradation.
  • Можетли проблема повториться вдругих модулях или компонентах системы? Что сделать, чтобы этого непроизошло? Например, актуализировать таймауты, добавить обработку долгого ответа.
  • Естьли платформенное решение, которое помогает избегать таких проблем? Возможно пора начать импользоваться? Врешении таких проблем часто помогают тестохранилка или PaaS.
  • Можетли необходимые для решения проблемы действия сделать команда или нужен отдельный проект, объединяющий несколько команд?

Основная ценность, которую мыполучаем отвстреч это action items, тоесть действия, направленные нато, чтобы проблема невозникала вдальнейшем или чтобы мыраньше еёзамечали ибыстрее чинили. Это могут быть как правки вкоде, так ипроекты поулучшения инструментов или процессов, предложения новых best practices иинформационные рассылки навсю инженерную команду.


Хорошие action items:


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

Плохие action items:


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

Мыдоговариваемся окусочках, которые надо сделать обязательно, итаких, которые сделать желательно. Когда action items сформулированы, мызаносим ихвJira это тикеты вразных проектах, слинкованные спостмортемом. Вначале описания необязательных задач ставим вопросительный знак, чтобы ихможно было сразу отличить. Поостальным считаем, что ихжелательно сделать втечение месяца. Чтобы это работало, яотдельно слежу завыполнением всех action items ипериодически прохожусь снапоминаниями полюдям, которым они назначены.


Как ведём базу знаний


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


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



Аещё поLSR можно строить любопытную аналитику


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


Лирическое заключение


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


Протестировать абсолютно всё нереально, поэтому вкакой-то момент надо сказать, что мыпротестировали достаточно хорошо, иостановиться. Ачто такое достаточно хорошо? Если врелизе нашли 100 багов это достаточно? Аесли избагов 20критичных, 50средних, аостальные некритичные это достаточно хорошо или недостаточно? Проблема втом, что ответ насамом деле зависит неоттого, сколько багов нашли, аоттого, сколько ненашли тестировщики, нозаметят пользователи.


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

Подробнее..

Стажировка в Авито глазами стажёра

14.04.2021 12:16:12 | Автор: admin

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

Но ведь самое интересное это процесс! Поэтому я решил рассказать о своей стажировке вАвито: как она проходит, какие ожидания от стажёра, какие задачи нужно решать. В статье я дам несколько основанных на моём опыте советов для тех, кто впервые собирается пойти стажироваться.

Поиск стажировки и собеседования

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

Из этого вытекают мои основные пожелания к стажировке:

  1. Возможность участвовать в процессах команды, а не быть в стороне.

  2. Руководство наставника, который может помочь, подсказать и направить в нужную сторону.

  3. Наличие code review. Знать инструмент это одно, а грамотно пользоваться им другое. Хочется, чтобы старшие коллеги ревьювили код.

  4. Упор на развитие стажёра, а не просто на полную эксплуатацию.

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

Сами этапы собеседования достаточно стандартные. Сначала нужно сделать тестовое задание. Если оно выполнено хорошо, дальше следует техническое собеседование, приуспешном прохождении которого можно попасть на последний этап собеседование стимлидом и HR. У меня техническое собеседование было без больших сюрпризов: немного лирики про структуры данных и алгоритмы, непосредственно сам Python, базы данных и теория, и практика с простенькими SQL-запросами. Ещё поговорили про конкурентность и вообще то, как работает ОС. В принципе, ничего суперсложного, однако всё равно стоит хорошенько подготовиться: часто бывают вопросы со звездочкой при успешном ответе наосновной вопрос.

Будьте общительнее на собеседовании. Без этого, скорее всего, искра не появится.

Я вышел на работу в команду Market Intelligence. Мы занимаемся построением ETL (extract, transform, load) процессов, то есть добычей данных. Для этого мы развиваем свою платформу, чтобы удобно можно было управлять кроулерами, которые добывают данные, развиваем свой фреймворк и пишем микросервисы.

Первые дни стажировки

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

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

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

К счастью, есть наставник. Он здорово помогал мне адаптироваться: рассказывал, зачем нужна каждая из встреч и как всё устроено внутри команды. Он же всегда подталкивал кдополнительным активностям, например, предложил подготовить доклад для внутреннего Python community. Ещё раз в неделю у стажёра и наставника проходят встречи один на один. Они нужны для получения обратной связи, как для тебя, так и для менеджера. На них можно скорректировать какие-то процессы, план развития и просто поговорить о том, как продвигается стажировка, получив ценные советы. Плюс у нас регулярно проходят встречи стажеров, что тоже помогает в адаптации.

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

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

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

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

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

Обучение и развитие

Работа над развитием стажёра может сильно варьироваться в зависимости от наставника, но, вероятно, суть будет одна. Я считаю, что мне очень повезло с наставником и за свой рост я вомногом благодарен ему. Мой план развития мы составили по методике OKR. В него входили как хард скиллы, такие MongoDB, Docker, Golang и т.д., так и софт, например, Agile и Kanban. Такой формат мне кажется успешным: на дистанции он приносит большие результаты.

Назвать технологии, которые хочется затащить, легко, но как всё-таки их изучать?

Образование внутри Авито

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

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

Лично я проходил внутренние курсы по tech onboarding и Agile, которые сильно помогли впервые дни. В них рассказывается, по каким правилам у нас всё работает и что где можно подсмотреть. А ещё прошёл курсы по Golang: они неплохо погрузили не только в сам язык, но и то, как его применяют именно в Авито.

Обучение вне Авито

Не стоит очевидно зацикливаться на одном. Что-то стоит изучать снаружи. Как показывает мой опыт, самые лучшие курсы делают сами компании, которые разрабатывали технологию. Обычно они называются University. Например, Redis, Mongo University. Лично мне они очень понравились, довольно хорошо погружают. Из минусов можно назвать только то, что они полностью на английском, но а как без него?

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

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

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

Стажировка после адаптации

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

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

Вывод

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

Подробнее..

Разрабы работают медленно и дорого и люди считают нас лентяями. Просто в разработке всё сложно

28.10.2020 18:06:09 | Автор: admin

Люди не изиндустрии вечно не понимают программистов: что они там такое сложное делают, если видно только две кнопки? Что за непонятные слова говорят? Почему так много получают?


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



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


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


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


Нам всем приходится работать сне-инженерами, дляних вся сложность существует только на наших словах другого способа осознать её уних просто нет. И, конечно же, нам вечно не верят. Как будто нам не хватает собственного синдрома самозванца?!


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


Почему ты всегда всё делаешь вдва раза дольше, чем обещаешь?


Потому что создатели Agile не верят, что можно делать что-то больше недели. Вот и говорю, вочто поверят.


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


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


Зачем ты мудришь?! Сделай, чтобы просто работало!


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


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


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


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


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


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


Разработчики думают наперёд, и это удорожает разработку вразы. Но если бы не думали, подорожало бы вдесятки десятков раз.


Почему вы просто не возьмёте и не придумаете уже один нормальный язык, на котором все будет работать?


А почему люди не начнут говорить на одном языке?


А если серьёзно: унас тысячи классов задач. Разные языки по-разному подходят подразные задачи. Унас миллионы очень разных разработчиков. Часть изних отлично подойдёт, чтобы делать простенькие приложения, а часть чтобы пилить рокет саенс. Вторым нужен очень сложный язык, иначе получится непроизводительно. А первые такой просто не смогут освоить. А нужны-то и те и другие!


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


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


Опять баги! Почему вы не можете писать без них, неучи?


Баги это чтобы бизнес не подумал, что сможет обойтись безнас.


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


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


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


Какой ещё новый язык? Какой ещё современный фреймворк? Нам не доигрушек! Почему не можете просто писать начём есть?


Работа должна приносить удовольствие. Мы хотим поиграть вигрушки.


А если серьёзно: все новые инструменты появляются только длятого, чтобы решить проблему, которая не решена встарых. Мы боремся задесятые доли процента оптимизации, и вцелом, это вообще единственный способ борьбы схаосом роста требований и индустрии. Мы отвечаем этому хаосу мощью и универсальностью своих новых инструментов. И надистанции видим подход работает. Чтобы сделать простое десктопное окно настаром WinApi, надо было написать сто строк кода. А сейчас одну.


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


Почему программисты так много получают? Ходите, кофе пьёте! За что вам платят!?


Потому что можем.


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


А кроме автоматизации мы ещё делаем вещи, которыми люди буквально живут.


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


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


Мы любим жаловаться, что джуны ничего не знают, но заэтим ничего уже есть гигантское количество знаний и нейронных связей. Досвоей первой работы я год корпел надконсольными шахматами, которые писал, чтобы изучить С++. Там было несколько тысяч строк кода, они работали каким-то чудом, потому что страшно толстого талмуда Программирование наязыке С++ не хватило, чтобы ответить даже намалую часть моих вопросов, как делать. Форумы, стековерфлоу, разные версии компилятора, стандарты, разные IDE те дни были большим мучением длямоих мозгов.


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


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

Подробнее..

Исследование про исследователей что мы узнали

01.02.2021 16:08:09 | Автор: admin

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


Нам было интересно увидеть картину порынку исравнить еёстем, как всё выглядит вАвито.



Формальности поопросу


Мысфокусировались наопыте исследователей UX, продуктовых или маркетинговых исервис-дизайнеров.


Ответы собирали с23.12.20 по15.01.2021. Всего анкету заполнил 251человек. Кроме двух вопросов оместе работы идолжности, остальные были необязательными, поэтому наних мыполучили меньше ответов.


Демографический портрет респондентов


Средний возраст респондентов 30лет, общий разброс от20до44лет. Большинство (82%) живут вМоскве, 7% вСанкт-Петербурге, 4% вЕкатеринбурге.



Большинство респондентов занимаются исследованиями больше 5лет, либо от1года до3лет.



Восновном наши респонденты работают втекущей компании меньше года (43%), треть отгода до2лет.



Погрейдам мыполучили большую часть ответов отведущих или старших (40%) иmiddle-исследователей (32%).


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

Вовсех следующих смысловых блоках уреспондентов была возможность выбирать несколько вариантов ответа.


Что мыузнали окомпаниях, вкоторых работают исследователи?


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


Большинство респондентов (41%) работает вкрупных компаниях, где больше 2000сотрудников.


Количество исследователей вкомпании показало большой разброс. Всреднем вкомпаниях по9исследователей (медиана 5, мода 3). Разброс мыувидели ивколичестве исследований вмесяц, которое приходится наодного исследователя (медиана 3имода 2, почти треть респондентов).


Для сравнения, вАвито сейчас работают 5исследователей иболее 40продакт-менеджеров. Мыхотим уменьшить нагрузку накаждого исследователя ирасширяем команду, поэтому ищем ещё ребят. Ссылка на вакансию вконце статьи.


Большинство исследователей (55%) работают более чем спятью заказчиками. Натекущий момент вАвито примерно такаяже ситуация: накаждого исследователя приходится от4до8команд внутри продуктового кластера. Мыпоняли, что такое соотношение непозволяет исследователю уделять достаточное внимание ипродукту, иработе над задачами, поэтому пришли крешению, что наодного исследователя должно быть ровно 4команды.


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


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


Формат работы исследователей


Больше половины респондентов (57%) работают вформате один исследователь напродуктовое направление инесколько команд. Текущая модель UXlab вАвито работает так же. У45% принят формат внутреннего агентства, когда самые разные команды привлекают исследователей напроекты.36% работают внутри одного продукта.


Кто исследует?


Ожидаемо, что вовсех компаниях качественные исследования проводят, собственно, исследователи. Ноу36% респондентов этим занимаются также продакт-менеджеры, ау28% дизайнеры.18% привлекают агентства или фрилансеров.


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


Кто проводит количественный этап?


74% респондентов отметили, что количественные исследования проводят UX-исследователи, навтором месте маркетинговые исследователи (62%), аучетверти респондентов этим занимаются аналитики.



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


Также для локальных задач UX-исследователи подключают команду колл-центра. Это помогает запустить опрос на100 человек иполучить результаты запару дней.


Как замеряют реакцию пользователей наизменения впродукте?


Напервом месте, конечноже, аналитика (78%), следом идут обращения вподдержку иопросы (по70%).



Каких навыков нехватает коллегам издругих функций?


Больше половины респондентов считают, что уихколлег издругих функций страдают навыки формулирования гипотез (53%) идоведение изменений допродукта поитогам исследований (52%). Чуть отстаёт отних, нотакже входит втоп-3, постановка задачи наисследование (49%).



Что делают кроме исследований?


Помимо непосредственно исследований, коллеги внедряют новые инструменты (63%), распространяют знания опользователях внутри компании (63%), оптимизируют процессы (60%) иобучают команды исследованиям (53%).



Как учат коллег исследованиям?


Восновном входе проектов (86%). У45% есть внутренний курс пообучению исследованиям, а15% отправляют коллег учиться навнешних ресурсах.


Награфике ниже можно увидеть, чему именно исследователи обучают коллег:



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


Поэтому, если увас есть курс или блок обучения потеме качественных исследований, ивыготовы провести его для команд Авито завознаграждение, напишите Михаилу: mmpravdin@avito.ru. Обсудим формат иусловия сотрудничества.


Как оценивается качество работы исследователей?


Лидирующий способ оценки качества работы исследователей отзывы коллег (69%), анаименее популярный количество исследований (20%). Примерно равнозначны улучшение процессов иинструментов (45%), влияние напродукт (43%) ипрогресс поличному плану развития (41%).



Как исследователи делятся знаниями смиром?


Восновном, либо никак, либо обучая коллег (по42%). Некоторые выступают наконференциях (17%), пишут статьи (14%), преподают начужих курсах (13%) или вуниверситете (11%).



Проблемы наработе


Явного лидера всписке трудностей нет, всех беспокоит разное, ноунаёмных сотрудников первое место занимает перегрузка задачами, ауфрилансеров нечёткость целей. Интересно, что уисследователей относительно редко встречается проблема карьерного роста иихредко неустраивает зарплата (15% и19% соответственно).



Как исследователи работают над проектами


Ещё мызадавали вопросы осамих исследованиях: методах ихпроведения, общении среспондентами, поиске дополнительных данных иоформлении результатов работы.


Методы исследований


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


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



Накаком этапе развития продукта чаще всего проводятся исследования?


Топ-3 исследование целевой аудитории, полностью работающий продукт истадия макетов.



Скем взаимодействуют исследователи?


Сотрудники, скоторыми чаще всего контактирует исследователь, это продакт-менеджеры (84%), дизайнеры (68%), другие исследователи (59%), аналитики (50%) исотрудники отдела маркетинга (48%). Если говорить про исследователей вагентствах, тоихтоп такойже, нонапервом месте другие исследователи, анеменеджеры.


Про опыт фасилитации


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



Общение среспондентами


Сейчас среди способов связи среспондентами безоговорочное лидерство забрал Zoom (94%). Треть респондентов также используют Skype (33%), ачетверть умудряется встречаться очно.


Модерация исследований инаблюдатели


Более половины респондентов (67%) сами модерируют поля иоколо трети ответили, что вэтом участвует команда (28%). При этом у40% исследователей вполях наблюдатели бывают очень часто, у32% присутствуют пару раз. Варианты никогда инакаждом интервью/тесте набрали по13% ответов.


Поиск респондентов


Кто ищет. Более половины исследователей ищут респондентов силами внешних рекрутёров, половина пользуется панелями/базами/сообществами ипочти половина ищут сами.37% отметили, что поиском респондентов для них занимаются другие сотрудники компании. При этом самостоятельно ищут чаще винхаусе, нежели вагентстве (35% против 26%).


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



Где ищут. Лидирующий источник собственные базы данных (66%), примерно равнозначны опросы или уведомления впродукте (48%) иемейл-рассылка (45%). Платными панелями иличными контактами пользуются около трети респондентов (32% и27% соответственно).



Дополнительные данные напроектах


Больше половины респондентов впроцессе работы над проектом обращаются каналитике (72%), смотрят информацию оконкурентах (67%) ичитают обращения вподдержку (59%).


Менее популярны чтение отзывов винтернете/магазинах приложений (45%), общение сменеджерами попродажам (42%) изнакомство срезультатами опросников NPS/CSI (41%).


При этом, аналитику чаще смотрят несами (самостоятельно смотрят23% респондентов), аставят задачу аналитику (больше половины опрошенных, 54%).




Оформление результатов


Лидируют старые добрые презентации, ноиMiro неотстаёт. Эти два способа выбирают половина респондентов.



Чем заканчиваются проекты для исследователя?


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



Сохранение иобмен знаниями оклиентах


Восновном есть база отчётов (75%) иорганизация встреч (51%), накоторых командам рассказывают оклиентах. Треть респондентов (32%) ведут корпоративный канал или блог иотносительно редко исследователи разрезают отчёт наотдельные инсайты (19%).



Когда другим сотрудникам нужна информация изисследований, они предпочитают обратиться кисследователю занужными ссылками (84%). 58% респондентов говорят, что коллеги сами ищут нужные отчёты вбазе и44% исследователей помогают командам сосбором нужной информации изразных отчётов.



Также мыпросили понекоторым вопросам дать оценку пошкале от1до10


Так мы узнали, что:


  • На7исследователи оценили уровень внедрения исследований впроцесс создания продуктов.
  • Втоже время, качество использования результатов коллеги оценили чуть ниже на6,7.
  • Влияние исследователей напродукты оценили всреднем на6,9.
  • Рекомендовать свою компанию как отличное место работы всреднем готовы на7,4 (мода 10).

А блиц-опрос вконце показал, что:


  • Большинство исследователей хотелибы работать впарт-тайм режиме (53% ответивших). Около25% вообще нехотелибы возвращаться вофис.


  • Топ-3 любимых источника знаний обисследованиях Medium (23%), телеграм-канал UXHorn (19%) исайт Nielsen Norman Group (17%).


  • Любимая одежда наудалёнке это футболка ипижама.


  • Среди любимых инструментов для работы лидирует Miro его назвали33% респондентов. МывАвито солидарны сбольшинством итакже делаем отчёты вMiro.


  • Топ-5компаний, вкоторых хотелибы работать исследователи. Это Яндекс (46%), Mail.ru Group (31%), Miro (31%), Авито (30%), Озон (23%).

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



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


  • Среди советов куда пойти учиться лидируют рекомендации найти стажировку/работу или реальный проект (36%), НИУ-ВШЭ (34%) истать психологом/социологом влюбом вузе (19%).


  • Большинство UX-исследователей хотят научиться работе сданными. Изтоп-5навыков, которые они хотелибы приобрести, 4относятся кработе сколичественными данными. Это анализ данных (15%), количественные исследования (12%), статистика (9%) иPython/R/SQL (8%). Также втоп-5 попало желание научиться проектированию интерфейсов (9%).




Про деньги


Мынеобошли стороной ивопрос сколько тызарабатываешь. Вот что получилось:



Сайд-проекты: даили нет?


Тех, кто работает вштате, мыспрашивали, берутли они сторонние проекты наисследования всвободное отработы время. Оказалось, что большинство UX-исследователей неберут сторонние проекты (38%). Еще13% готовы только консультировать, нонеделать проекты руками. Оставшиеся48% готовы временно поработать навнешних проектах, но18% неуспевает совмещать ихсосновной работой, а13% ненаходят таких проектов изадач.



Эти данные совпадают снашими результатами опроса впроекте облачные исследователи. Там мыузнали, что70% исследователей сейчас имеют постоянную занятость, нохотят иготовы попробовать себя навнешних проектах. Напомним, что для таких исследователей мысделали сообщество, где публикуем проекты отразных компаний: Яндекс, Mail.ru, Авито, Сбер, Joom ипр. Если выпопали вгруппу хочу, ждём вас внашем сообществе.


Минутка благодарностей


Спасибо Тане Чернявской запрограммирование опроса ианализ данных, Мише Правдину заидею иподготовку вопросов, атакже всем кто помогал сфинальной анкетой иеераспространением: всей команде UXlab Авито, Даше Хлоповой, Алине Ермаковой, Наташе Спрогис, Диме Соловьеву, Анне Кон, Максиму Козлову иМише Хананашвили.


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


Ссылки для исследователей


  1. Подробнее посмотреть результаты исследования.
  2. Присоединиться к сообществу облачных исследователей.
  3. Стать частью исследовательской команды Авито.
Подробнее..

Материалы с митапа для аналитиков роль аналитика в развитии продуктов

26.03.2021 16:04:17 | Автор: admin

Недавно прошёл наш митап для аналитиков, а значит, пора делиться презентациями и видеозаписями выступлений. В них спикеры из Skyeng, Ситимобил и Авито на боевых примерах показывают пользу аналитики для запуска, тестирования и развития продуктов.

Поиск точек роста в продукте с помощью аналитики на примере Избранных продавцов Иван Жучков, Авито

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

00:00 Представление спикера и плана доклада

01:08 Продукт: Избранные продавцы

02:16 Стартовые метрики и первые проблемы с ними

04:13 Рекомендации подписок на продавцов

08:10 Воронка продукта и инициативы из неё

11:13 Применение анализа поведения пользователей

13:54 Сравнительная ценность подписок на продавцов

16:31 Итоговый рост метрик продукта

17:41 Ответы на вопросы

Посмотреть презентацию Ивана

Оценка потенциала кикшеринга в сервисе Ситимобил Андрей Лекомцев, Ситимобил

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

00:00 Представление спикера и темы

00:30 Проблемы руководителей проектов и воронка продуктовой фичи

02:32 Что такое кикшеринг и в чём его особенности

04:25 Оценка задачи по интеграции самокатов

09:40 Как использовали результаты

10:20 Ответы на вопросы

Посмотреть презентацию Андрея

Аналитика в продуктовой разработке на примере Автопубликации объявлений Мария Перетрухина, Авито

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

00:00 Представление спикера и план доклада

00:43 Проблема: упущенный контент в объявлениях

04:12 Оценка потенциального прироста базы контента

05:33 Поиск решения: как терять меньше контента

07:07 Проверка гипотезы с помощью MVP

10:30 Многоразовая автопубликация: оценка и предотвращение рисков

17:35 Результаты внедрения Автопубликации

20:46 Ответы на вопросы

Посмотреть презентацию Марии

Поиск точек роста в новом продукте с помощью алертов Михаил Михайлов, Skyeng

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

00:00 Представление спикера и темы

01:57 Skyeng до нового продукта

04:06 Идея нового премиум-продукта и поиск составляющих для него

10:13 Внедрение алертов

15:49 Как устроена работа с алертом: кейсы из практики

21:51 Выводы: как аналитик может помочь продукту выстрелить

22:48 Ответы на вопросы

Посмотреть презентацию Михаила

На сегодня всё. До встречи на новых митапах!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    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