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

Блог компании контур

Транспортный агент MS Exchange для защиты от вирусов и нежелательной почты

18.06.2021 12:04:53 | Автор: admin

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

Пример зловреда, с которым встроенные механизмы Exchange не справилисьПример зловреда, с которым встроенные механизмы Exchange не справились

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

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

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

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

История разработки

Сначала мы дописывали StripLinks, который в качестве примера транспортного агента представлен Microsoft, а затем прикрутили к нему функциональность опенсорсного ExchangeAttachmentFilter для фильтрации по имени файлов. Этого нам показалось мало и мы добавили туда анализ контента файлов алгоритмом Ахо-Корасик и регулярками, анализ текста, заголовков и темы письма, а еще обезвреживание автозапуска в pdf.

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

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

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

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

Как это работает

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

  • Совпадение отправителя и получателя письма

  • Опасные шаблоны в теме письма

  • Ссылки в тексте письма

  • Опасные шаблоны в тексте письма

  • Подделку MessageID

  • Потенциально опасные заголовки письма

  • SMTP сессию

  • Проверку вложения по имени

  • Проверку вложения YARA на наличие вредоносных сигнатур

  • Проверку содержимого архивов остальными файловыми модулями

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

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

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

  • Пропустить

  • Отклонить

  • Добавить в тему письма надпись "опасно"

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

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

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

Что мы хотим от публикации в опенсурс?

  • Идеи, как можно было бы улучшить/поправить инструмент

  • Помощь энтузиастов в развитии агента

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

Подробнее..

Анонс Code Challenge недельное соревнование для настоящих разработчиков

06.07.2020 16:22:04 | Автор: admin

image


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


Когда: 6-12 июля
Где: онлайн


Что случилось?


Code Challenge первое открытое онлайн-соревнование от Контура для хардкорных инженеров. Уже сейчас открыта песочница, а сам контест начнётся вечером, в понедельник 6 июля, и продлится 7 суток. Вместе мы попытаемся прорваться к центру вселенной через вражеские корабли и планеты древних цивилизаций. Конечно, с помощью кода и квестов.


Если вы уже слышали про соревнования ICFP Contest, CodinGame или Google Code Jam, вам точно понравится то, что мы приготовили.


А в чём суть контеста?


В каждом уголке галактики непрерывно идут бои за первенство. В боях участвуют флоты под управлением тактических ботов. Мы будем программировать своего бота (на C#, JS, Python или 1С %)), управлять космическими кораблями, побеждать врагов и захватывать новые планеты. Прокачать тактического бота помогут артефакты древних цивилизаций, но найти их будет непросто: все они спрятаны и зашифрованы.


Почему это интересно?


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


Кто может участвовать?


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


Как стартануть?


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


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


А если победим?


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


Герои меча Брезенхэма и магии Чебышёва, в бой!

Подробнее..

Контур стал организатором ICFPC 2020

07.07.2020 10:10:24 | Автор: admin

Ничего не планируйте с 17 по 20 июля, потому что в это время пройдет ежегодное международное соревнование ICFPC 2020. Собирайте команду и трое суток решайте секретную задачу от Контура. Чтобы быть в курсе всех новостей, получать подсказки и не пропустить регистрацию, подписывайтесь на Твиттер.


15 лет команда Контура участвовала в соревновании, а в этом году нас пригласили провести ICFPC 2020. Мы первая команда из России, которой доверили организацию, и это очень круто! Какую задачу мы приготовили пока секрет. Все участники узнают ее условия одновременно 17 июля, но уже сейчас в Твиттере можно увидеть некоторые спойлеры.




Почему нужно участвовать в ICFPC 2020


ICFPC командное соревнование. Соревнований для одиночек много: например, Facebook Hacker Cup и Google Code Jam. Если вам нравятся AI для игр, то codingame.com проводят отличные челенджи раз в 2-3 месяца. В одиночных соревнованиях топ обычно забит какими-то гениями, а в командных можно хорошо выступить за счет упорства и хорошей организации.


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


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


Задачи небанальные. Организатор соревнования меняется каждый год и старается превзойти предыдущего, поэтому задачи год от года становятся все интереснее. Обычно это университет, поэтому они добавляют в задачи отсылки к научным проблемам и классике computer science. Кроме того, ICFPC приурочен к научной конференции по функциональному программированию ICFP, и это влияет на задачи. Через раз приходится читать описания на функциональном псевдоязыке (не бойтесь, понятном для обывателей!), а потом программировать виртуальные машины и компиляторы.


Задача одна! За 72 часа команда неограниченного размера должна решить всего одну задачу. Но многогранную и трудную. Её нельзя решить оптимально, но можно решить лучше других команд. Самыми необычными и яркими задачами считаются задачи 2006 и 2007 годов, в которых балом правили виртуальные машины внутри виртуальных машин и а также реверс-инжениринг виртуальных машин.


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


Подробности соревнования появляются в Твиттере. Там же скоро появится ссылка на регистрацию.


Желаем удачи!

Подробнее..

Авалония для самых маленьких

26.11.2020 12:17:06 | Автор: admin
В свежем превью Rider, помимо прочего, появилась поддержка Авалонии. Авалония это самый крупный .NET фреймворк для разработки кроссплатформенного UI, и его поддержка в IDE отличный повод наконец разобраться, как писать десктопные приложения для любых платформ.

В этой статье я на примере простой задачи по реализации калькулятора покажу:

  • как управлять разметкой,
  • как связывать функциональность с компонентами,
  • как управлять стилями.



Подготовка


Для работы я использовал:


Единственным обязательным инструментов в этом списке является сам дотнет. Остальное можете выбирать сами: любимую операционную систему и IDE (например, тот же Rider).
Для инициализации проекта мы воспользуемся шаблонами .NET приложений для Авалонии. Для этого нам потребуется клонировать репозиторий с шаблонами, а затем установить скачанные шаблоны:

git clone https://github.com/AvaloniaUI/avalonia-dotnet-templates.gitdotnet new --install /path/avalonia-dotnet-templates/

Типы проектов Авалонии
Типы проектов

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

dotnet new avalonia.mvvm -o ACalc

Перейдем в директорию проекта и обновим все версии пакетов на самые новые (на момент написания статьи):

dotnet add package Avalonia --version 0.10.0-preview6dotnet add package Avalonia.Desktop --version 0.10.0-preview6dotnet add package Avalonia.ReactiveUI --version 0.10.0-preview6

Давайте внимательнее посмотрим на структуру проекта, сгенерированную шаблоном:

image

  • В папке Assets хранятся ресурсы, используемые нами в данном проекте. На текущий момент там лежит лого Авалонии, использующееся в качестве иконки приложения.
  • В папку Model мы будем складывать все общие модели, используемые в нашем приложении. На текущий момент она пуста.
  • Папка ViewModels предназначена для хранения логики, которая будет использоваться в каждом из окон. Прямо сейчас в этой папке хранится ViewModel главного окна и базовый класс для всех ViewModel.
  • В папке Views хранится разметка окон (а также code behind файл, в который хоть и можно положить логику, но лучше для этих целей использовать ViewModel). На текущий момент у нас есть только главное окно.
  • App.xaml общий конфиг приложения. Несмотря на то, что он и выглядит как еще одно окно, на самом деле, этот файл служит для задания общих настроек приложения.
  • ViewLocator нам в этот раз не пригодится, так как он используется для создания кастомных контролов. Подробнее о нем можно почитать в документации Авалонии.

Запустим наше приложение командой dotnet run.



Теперь все готово для разработки.

Разметка


Начнем с создания базовой разметки. Перейдем в файл Views/MainWindow.xaml там будет храниться разметка главного окна нашего калькулятора.



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

Итак, заменим TextBlock на пустой Grid:

<Grid></Grid>

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

<Grid>    <Grid.RowDefinitions>        <RowDefinition Height="auto"></RowDefinition>        <RowDefinition Height="auto"></RowDefinition>        <RowDefinition Height="*"></RowDefinition>    </Grid.RowDefinitions></Grid>

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

<Grid>    <Grid.RowDefinitions>        <RowDefinition Height="auto"></RowDefinition>        <RowDefinition Height="auto"></RowDefinition>        <RowDefinition Height="*"></RowDefinition>    </Grid.RowDefinitions>    <!--строка меню-->    <Menu>    </Menu>    <!--Импровизированный экран нашего калькулятора-->    <TextBlock>    </TextBlock>    <!--Grid для клавиш-->    <Grid></Grid></Grid>

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

<Grid>    <Grid.RowDefinitions>        <RowDefinition></RowDefinition>        <RowDefinition></RowDefinition>        <RowDefinition></RowDefinition>        <RowDefinition></RowDefinition>        <RowDefinition></RowDefinition>    </Grid.RowDefinitions>    <Grid.ColumnDefinitions>        <ColumnDefinition></ColumnDefinition>         <ColumnDefinition></ColumnDefinition>         <ColumnDefinition></ColumnDefinition>         <ColumnDefinition></ColumnDefinition>         <ColumnDefinition></ColumnDefinition>    </Grid.ColumnDefinitions>    <Button Grid.Row="0" Grid.Column="0">1</Button></Grid>

Стоит отметить, что элементы внутри Grid могут занимать несколько ячеек. Для этого используются параметры ColumnSpan и RowSpan:

 <Button Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2">=</Button>

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

Последнее, что нам осталось сделать это задать параметры окна. Установим стартовые и минимальные размеры окна (они задаются в корневом элементе Window).

MinHeight="300"MinWidth="250"Height="300"Width="250"

После добавления всех элементов разметки наше окно калькулятора будет выглядеть так:



Основной функционал


С разметкой закончили, пора реализовать логику!

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

public enum Operation{    Add,    Subtract,    Multiply,    Divide,    Result}

Теперь перейдем в класс ViewModel/MainWindowViewModel. Здесь будет храниться основная функциональность нашего приложения.

Добавим в файл несколько приватных полей, с которыми мы будем работать:

private double _firstValue;private double _secondValue;private Operation _operation = Operation.Add;

Теперь реализуем основные методы:

  • AddNumber добавляет новую цифру к числу.
  • ExecuteOperation выполняет одну из операций, описанных в енаме Operation.
  • RemoveLastNumber удаляет последнюю введенную цифру.
  • ClearScreen очищает экран калькулятора.

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

Связывание


Теперь, когда у нас готовы и разметка, и логика, пора связать их друг с другом.
В Авалонию по умолчанию включен Reactive UI это фреймворк, предназначенный как раз для связывания View и Model при использовании MVVM. Подробнее о нем вы сможете прочитать на официальном сайте и в документации Авалонии. Конкретно сейчас нас интересует возможность фреймворка обновлять View при изменении данных.

Для хранения актуального значения, выводимого на экране, реализуем свойство ShownValue:

public double ShownValue{    get => _secondValue;    set => this.RaiseAndSetIfChanged(ref _secondValue, value);}

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

Привяжем это свойство к созданному на этапе разметки текстовому полю:

<TextBlock Grid.Row="1" Text="{Binding ShownValue}" />

Благодаря директиве Binding и методу RaiseAndSetIfChanged значение свойства Text в этом поле будет обновляться при каждом изменении значения свойства ShownValue.

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

public ReactiveCommand<int, Unit> AddNumberCommand { get; }public ReactiveCommand<Unit, Unit> RemoveLastNumberCommand { get; }public ReactiveCommand<Operation, Unit> ExecuteOperationCommand { get; }

Команды нужно инициализировать в конструкторе класса, связав их с соответствующими методами:

public MainWindowViewModel(){    AddNumberCommand = ReactiveCommand.Create<int>(AddNumber);    ExecuteOperationCommand = ReactiveCommand.Create<Operation>(ExecuteOperation);    RemoveLastNumberCommand = ReactiveCommand.Create(RemoveLastNumber);}

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

<Button Grid.Row="3" Grid.Column="2" Command="{Binding RemoveLastNumberCommand}"></Button>

Несколько сложнее дела обстоят с номерными кнопками и кнопками операций. Для них мы должны передать в качестве параметра вводимую цифру или операцию. Для этого в корневом теге Window нам нужно добавить пространство имен System:

xmlns:s="clr-namespace:System;assembly=mscorlib"

А затем обновить разметку кнопок, добавив в них связанный метод и параметр:

<Button Grid.Row="0" Grid.Column="0" Command="{Binding AddNumberCommand}">    <Button.CommandParameter>        <s:Int32>1</s:Int32>    </Button.CommandParameter>     1</Button>

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



Стили


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

В Авалонии есть три способа управлять стилями:

  • настроить стили внутри компонента,
  • настроить стили в рамках окна,
  • подключить пакет стилей.

Пройдемся по каждому из них.

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

<TextBlock Grid.Row="1" Text="{Binding ShownValue}" TextAlignment="Right" FontSize="30" />

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

<Window.Styles>    <Style Selector="Button">        <Setter Property="Margin" Value="5"></Setter>     </Style></Window.Styles>

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

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



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

dotnet add package Material.Avalonia --version 0.10.3

А теперь обновим файл App.xaml и укажем в нем используемый пакет стилей и его параметры.

<Application ...             xmlns:themes="clr-namespace:Material.Styles.Themes;assembly=Material.Styles"             ...>    <Application.Resources>        <themes:BundledTheme BaseTheme="Dark" PrimaryColor="Purple" SecondaryColor="Amber"/>    </Application.Resources>    <Application.Styles>        <StyleInclude Source="avares://Material.Avalonia/Material.Avalonia.Templates.xaml" />    </Application.Styles></Application>

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



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

Заключение


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

Все исходники проекта вы можете найти в репозитории на Github.

На этом все! Оставайтесь на связи, мы вернемся со статьями о более продвинутых возможностях Авалонии.
Подробнее..

Вышел .NET 5. И что?

04.12.2020 10:17:32 | Автор: admin

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


image


Перфоманс


Изменения в перфомансе это самая заметная часть релиза. О них много писали на сайте Microsoft и рассказывали на .NET Conf. Давайте посмотрим внимательнее и определимся, где, насколько и за счет чего выросла производительность.


image


Измерения перфоманса с этого скрина основаны на бенчмарках от TechEmpower. Здесь Microsoft сравнивают результаты замеров производительности .NET 3.1 с ожидаемыми значениями для .NET 5. Стоит заметить, что это именно ожидаемые значения реальных результатов от TechEmpower пока не было. Однако уже эти результаты выглядят впечатляюще ускорение на 38 процентов для Plaintext, на 42 для Json и на 20 процентов в категории Fortunes (пояснения по разным типам бенчмарков вы можете найти в вики TechEmpower).


Помимо результатов бенчмарков от TechEmpower, Microsoft также рассказывает и о других улучшениях. Например, сериализация JSON стала работать на 20% быстрее, а сериализация больших коллекций и массивов ускорилась аж в три раза.


Также замеры производительности делал автор библиотеки Fusion Александр Якунин. В двух статьях на Medium он сначала измерил, насколько ускорилась работа демок его библиотеки (от 20 до 105 процентов), а затем проверил пятый .NET на нескольких синтетических бенчмарках и также получил некоторое ускорение.


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


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


Изменения в JSON сериализации выглядят особенно впечатляюще. Только посмотрите на результаты бенчмарка по сериализации больших массивов!


private MemoryStream _stream = new MemoryStream();private DateTime[] _array = Enumerable.Range(0, 1000).Select(_ => DateTime.UtcNow).ToArray();[Benchmark]public Task LargeArray(){    _stream.Position = 0;    return JsonSerializer.SerializeAsync(_stream, _array);}

image


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

Языковое


Вместе с .NET 5 вышла и девятая версия C#. Официальную страничку с изменениями вы можете найти на сайте Microsoft, но давайте попробуем разобраться что именно здесь стоит внимания?


Самое заметное изменение это record-типы, позволяющие избавиться от бойлерплейта при написании DTO. Записи это иммутабельные ссылочные типы с простым и коротким объявлением. В них по умолчанию определены методы Equals, HashCode, Copy, Clone, PrintMembers и ToString разумеется, все они используют значения полей типа для выполнения операций. То есть Equals корректно сравнивает две записи по значениям, а не по ссылке.


Также записи поддерживают синтаксис копирования с изменением значений полей через with, например:


var person = new Person { FirstName = "Mads", LastName = "Nielsen" };var otherPerson = person with { LastName = "Torgersen" };

Больше о применении записей вы можете почитать в блоге Владимира Хорикова и в публикации от Konrad Kokosa.


Другое важное изменение это обновленный pattern matching. Теперь паттерны могут настраиваться по типам и операторам сравнения, причем несколько разных сравнений вы можете объединять через логические операторы. Например:


DeliveryTruck t when t.GrossWeightClass switch {    < 3000 => 10.00m - 2.00m,    >= 3000 and <= 5000 => 10.00m,    > 5000 => 10.00m + 5.00m}

Остальные изменения не столь заметны они касаются верхнеуровневых программ (возможность писать короткие программы без базового класса и функции main), упрощенного синтаксиса для new (без указания типа), init-only сеттеров и прочего.


Последние изменения в новом C#, которые хотелось бы упомянуть это расширенная поддержка для source generators. Этот функционал позволяет генерировать код при компиляции проекта и зачастую очень удобен для авторов библиотек. В новом релизе работу с генераторами кода сделали чуть проще, расширив функционал partial классов (с них сняли некоторые из старых ограничений) и добавив инициализаторы модулей (методы, которые вызываются до первого доступа к любому полю/методу модуля).
Хороший пример работы с генераторами кода вы можете найти в этой статье на Medium.


Отдельно стоит рассказать про F# 5. В новой версии языка добавилась масса классных возможностей для скриптов (например, директива для подключения nuget пакетов), slicing для более удобной работы с данными, а также улучшилась работа с quotation expressions и computation expressions. А еще сильно улучшился интероп с C# и перфоманс. Словом, новый релиз выглядит интересно и делает F# потенциально полезнее в продакшене. Полное описание всех изменений можно найти на сайте Microsoft.


Коротко язык оброс разнообразным сахаром. Самое заметное изменение в C# 9 это записи, но едва ли оно само по себе стоит обновления. С другой стороны, если вы будете обновлять версию дотнета по каким-то другим причинам, то у вас появится новый функционал, делающий язык чуть более мощным. Мелочь, а приятно.

Прочее


Помимо глобальных улучшений платформы Microsoft также неплохо поработали над отдельными библиотеками.


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


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


С кроссплатформенностью все осталось по-прежнему. Релиз MAUI перенесли на .NET 6, а других решений для кроссплатформенного десктопа Microsoft пока не предлагает. Так что продолжаем использользовать Авалонию (или можно обратить свое внимание на Uno Platform перспективно выглядящий фреймворк для разработки на все и сразу).


Еще больше улучшений появилось для фулстек веб-разработки с использованием Blazor. Главное из них это улучшения производительности Microsoft обещают, что WebAssembly версия Blazor ускорится аж в три раза. Одной из основных причин такого роста производительности стал пререндер на стороне сервера. А еще добавили ленивую подгрузку зависимостей, изоляцию CSS/JS в рамках файла, новые контролы и многое другое. В общем, если вы собирались попробовать Blazor, но все никак не доходили руки сейчас он выглядит куда более production-ready технологией.


image


Из оставшихся изменений заметнее всего смотрятся те, которые призваны помочь микросервисам и работе с облачными сервисами в целом. Здесь и уменьшение размеров Docker-контейнеров, и улучшенная поддержка gRPC, и поддержка OpenAPI по умолчанию, и многое другое. Кстати, Azure полностью поддерживает .NET 5 со дня релиза.


А еще Microsoft выкатили Project Tye, который как раз является инструментом для удобного управления микросервисами. На текущий момент трудно сказать, стоит ли использовать Tye в продакшене, но обратить внимание и поиграться с ним в свободное время определенно нужно.


Коротко Microsoft улучшили поддержку отдельных инструментов и добавили приятных фич для облака. Если вы используете WinForms, WPF или Blazor обновиться определенно стоит.

Миграция


Давайте разберемся, насколько сложно будет мигрировать ваше приложение с .NET Core 3 на .NET 5?


Гайд по миграции от Microsoft говорит нам, что основные сложности в миграции коснутся приложений на Blazor, там произошло много ломающих изменений. Для большинства же остальных приложений понадобится просто обновить версию дотнета в application.json, файл проекта и версии зависимостей. А еще адрес докер-контейнера с .NET 5 немного отличается от соответствующего адреса для .NET 3.1:


docker pull mcr.microsoft.com/dotnet/core/aspnet:3.1docker pull mcr.microsoft.com/dotnet/aspnet:5.0

Другие отзывы о миграции, которые я смог найти например, вот этот гайд от Alexandre Malavasi и твит о миграции Fusion от Александра Якунина тоже говорят, что миграция проходит без каких-то специфических трудностей. Я также не столкнулся с особыми проблемами, пока мигрировал свои проекты и демки на новый дотнет. В любом случае, перед миграцией на новую версию советую внимательно изучить список ломающих изменений. А еще стоит посмотреть доклад про миграцию с .NET Conf.


Итого


Подведем итоги. Стоит ли мигрировать на новый дотнет и ради чего?


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


Другие важные причины для миграции это улучшения для Blazor и WPF/WinForms. Если вы используете любой из этих фреймворков, стоит попробовать перейти на .NET 5, все же изменения достаточно заметные и полезные. Однако стоит учитывать, что для Blazor миграция выйдет достаточно непростой.


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


И на этом все! Удачной миграции на новый дотнет. Оставайтесь на связи, мы вернемся с более подробными материалами про современные возможности дотнета.

Подробнее..

От WPF к Авалонии

16.02.2021 12:20:24 | Автор: admin

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

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

Стили

На первый взгляд, стили в Авалонии выглядят точно также, как и в WPF они задаются в блоке Styles с помощью селекторов и сеттеров. Первые выбирают набор блоков, к которым применяются стили, вторые задают непосредственно стили. Давайте сравним два одинаковых стиля в WPF и Авалонии:

<Style TargetType="TextBlock">  <Setter Property="HorizontalAlignment" Value="Center" />  <Setter Property="FontSize" Value="24"/></Style>
<Style Selector="TextBlock"><Setter Property="HorizontalAlignment" Value="Center" /><Setter Property="FontSize" Value="24"/></Style>

Как видите, в данном фрагменте различается только объявление тега Style в WPF для выбора целевого блока используется параметр TargetType, а в Авалонии - Selector. Однако селекторы в Авалонии куда мощнее, чем TargetType в WPF. Больше всего они напоминают селекторы из CSS с классами, псевдоклассами и кастомными обращениями.

Например, вот так мы можем задать размер шрифта для всех текстовых блоков с классом h1

<Styles>  <Style Selector="TextBlock.h1">    <Setter Property="FontSize" Value="24"/>  </Style></Styles><TextBlock Classes="h1">Header</TextBlock>

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

<Styles>  <Style Selector="Button:pointerover">    <Setter Property="Button.Foreground" Value="Red"/>  </Style></Styles><Button>I will have red text when hovered.</Button>

И, конечно же, селекторы в Авалонии позволяют гибко выбирать целевые контролы для стилей через цепочки дочерних элементов, по совпадению нескольких классов, по шаблонам и по значениям определенных свойств. Опять же, это очень похоже на селекторы в CSS. Например, вот так мы можем выбрать кнопку, являющуюся прямым наследником элемента с классом block, имеющую значение свойства IsDefault = true:

.block > Button[IsDefault=true]

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

Обновленный синтаксис XAML

Как и в случае со стилями, синтаксис XAML не слишком сильно отличается от WPF. Объявление контролов, параметры, биндинги все выглядит по-прежнему. Однако некоторые отличия все же есть синтаксис стал более емким и понятным, а где-то добавились новые возможности. Посмотрим на изменения по порядку.

Начнем с упрощений в синтаксисе. Самый простой пример такого упрощения это объявление строк и столбцов в Grid. Классическое, привычное с WPF объявление будет выглядеть следующим образом:

<Grid>  <Grid.RowDefinitions>    <RowDefinition Height="*"></RowDefinition>    <RowDefinition Height="Auto"></RowDefinition>    <RowDefinition Height="32"></RowDefinition>  </Grid.RowDefinitions></Grid>

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

<Grid RowDefinitions="*,Auto,32,"/>

Упростилось и подключение зависимостей в XAML файлах. Теперь clr-namespace можно заменить на using. Такое изменение позволяет сделать подключение сторонних библиотек короче и читаемее.

Было: xmlns:styles="clr-namespace:Material.Styles;assembly=Material.Styles"

Стало: xmlns:styles="using=Material.Styles"

Другое любопытное изменение это вынесение DataTemplates и Styles в отдельные теги. Раньше они размещались внутри Resources.

<UserControl xmlns:viewmodels="clr-namespace:MyApp.ViewModels;assembly=MyApp">  <UserControl.DataTemplates>    <DataTemplate DataType="viewmodels:FooViewModel">      <Border Background="Red" CornerRadius="8">        <TextBox Text="{Binding Name}"/>      </Border>    </DataTemplate>  </UserControl.DataTemplates>  <UserControl.Styles>    <Style Selector="ContentControl.Red">      <Setter Property="Background" Value="Red"/>    </Style>  </UserControl.Styles><UserControl>

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

<TextBox Name="source"/><!-- Binds to the Text property of the "source" control --><TextBlock Name=other Text="{Binding #source.Text}"/>

Конструкция $parent позволяет обращаться к родительским компонентам.

<Border Tag="Hello World!">  <TextBlock Text="{Binding $parent.Tag}"/></Border>

Кстати, такое обращение поддерживает индексирование. Иначе говоря, конструкция $parent[1] позволит вам обратиться к родителю родителя вашего компонента. А конструкция $parent[0] эквивалентна $parent.

Помимо индексов здесь также можно использовать обращение по типу. $parent[Border] позволит вам обратиться к первому предку с типом Border. А еще такое обращение можно совместить с индексированием.

<Border Tag="Hello World!">  <Border>    <Decorator>      <TextBlock Text="{Binding $parent[Border;1].Tag}"/>    </Decorator>  </Border></Border>

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

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

<StackPanel>  <TextBox Name="input" IsEnabled="{Binding AllowInput}"/>  <TextBlock IsVisible="{Binding !AllowInput}">Sorry, no can do!</TextBlock></StackPanel>

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

<Panel>  <ListBox Items="{Binding Items}"/>  <TextBlock IsVisible="{Binding !Items.Count}">No results found</TextBlock></Panel>

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

<Panel>  <ListBox Items="{Binding Items}" IsVisible="{Binding !!Items.Count}"/></Panel>

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

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

<TextBlock Text="{Binding MyText}" IsVisible="{Binding MyText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>

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

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

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

<Application>  <NativeMenu.Menu>    <NativeMenu>      <NativeMenuItem Header="About MyApp" Command="{Binding AboutCommand}" />    </NativeMenu>  </NativeMenu.Menu></Application>

Декларативный UI via F#

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

Сообщество Авалонии разработало отличную библиотеку Avalonia.FuncUI, позволяющую вам писать UI на Авалонии в декларативном стиле. Получающийся код напоминает реализацию UI с помощью Elm или Jetpack Compose.

module Counter =    type CounterState = {    count : int  }  let init = {    count = 0  }  type Msg =  | Increment  | Decrement      let update (msg: Msg) (state: CounterState) : CounterState =   match msg with    | Increment -> { state with count =  state.count + 1 }    | Decrement -> { state with count =  state.count - 1 }  let view (state: CounterState) (dispatch): IView =    DockPanel.create [      DockPanel.children [        Button.create [          Button.onClick (fun _ -> dispatch Increment)          Button.content "click to increment"        ]        Button.create [          Button.onClick (fun _ -> dispatch Decrement)          Button.content "click to decrement"         ]        TextBlock.create [          TextBlock.dock Dock.Top          TextBlock.text (sprintf "the count is %i" state.count)        ]      ]    ]

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

Недостатки

Перейдем к самому интересному а что же не так с Авалонией в сравнении с WPF?

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

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

Ну и самая заметная проблема, о которой говорят многие это отсутствие гарантий. Авалония это инструмент, создаваемый исключительно сообществом, что не слишком-то привычно для .NET разработчиков. За ним не стоит Microsoft или какая-то другая крупная компания. В этих условиях многим не хочется рисковать, надеясь на open source продукт кто знает, вдруг завтра мейнтейнер потеряет интерес, и разработка встанет? Однако это же дает вам возможность заметно влиять на развитие Авалонии, исправляя существующие проблемы и предлагая новые фичи.

Заключение

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

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

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

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

Подробнее..

Как работать с иерархической структурой классов

22.04.2021 14:23:53 | Автор: admin

Задача классификации одна из самых известных в машинном обучении. Очень многие проблемы, решаемые с помощью ML, так или иначе сводятся к классификации распознавание изображений, например. И все выглядит просто и понятно, когда нам нужно определить объект в один из нескольких классов. А что если у нас не плоская структура из нескольких классов, а сложная разветвленная иерархия на 683 категории? Именно о таком случае мы сегодня и поговорим. Под катом рассказ о том, зачем в задачах классификации нужны сложные иерархии и как с ними жить.

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

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

Это звучит понятно в школьном учебнике, но в контексте машинного обучения возникает множество вопросов:

  • Как с этим всем обращаться?

  • Какие классы предсказывать?

  • Сколько моделей тренировать для решения задачи?

  • Как работать с данными?

  • Как вносить изменения в иерархию классов и как реагировать на эти изменения с точки зрения модели?

Все эти проблемы мы разберем на примере задачи классификации, которую мы решаем в Контуре.

Постановка задачи

Мы работаем с чеками. В каждом чеке может встретиться много разных товаров, которые можно сгруппировать множеством разных способов. На данный момент нам интересно группировать эти товары с помощью дерева категорий, которое мы будем называть KPC (Khajiit Product Classification). Это здоровенное дерево, состоящее из 683 категорий.

Для этих категорий у нас есть Golden Set, наш размеченный набор данных (штрихкод категория KPC) для обучения. В датасете почти три миллиона записей и мы постоянно работаем над его дополнением и улучшением разметки.

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

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

Разметка данных

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

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

  • Активный.

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

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

  • Удаленный.

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

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

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

  • другое/другие (например Молочные товары, сыры и яйцо (другое)) группа для товаров, которые относятся к родительской категории, но не относятся (возможно, пока) ни к одной из дочерних.

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

Добавление категории

Добавить категорию мы можем в любое время, но для того, чтобы товары начали в неё попадать, необходимо:

  • Добавить категорию в KPC.

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

  • Переобучить модель, чтобы она научилась классифицировать товары в новую категорию.

Удаление категории

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

Для удаления категории необходимо:

  • Перевести категорию в статус Архивная.

  • Решить, что мы делаем с товарами, которые относились к удаляемой и дочерним категориям.

  • Заменить удаляемую категорию у товаров в Golden Set.

  • Указать дочерним категориям новую родительскую категорию или её отсутствие (если дочерняя категория должна стать категорией верхнего уровня).

  • Переобучить модель, чтобы она перестала классифицировать товары в удаляемую категорию (и начала классифицировать эти товары в новые категории).

  • Перевести категорию в статус Удаленная.

Разбиение категории

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

  • Обновить категории в Golden Set, чтобы отнести товары к новым категориям.

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

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

Обучение модели

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

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

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

Алгоритм разрешения конфликтов

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

Для разрешения этих ситуаций мы будем маппить категории (то есть переносить все товары из одной категории в другую), а после обновлять наше дерево категорий (оставлять в рассмотрении только те категории, которые представлены в Golden set), проверяя, остались ли коллизии.

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

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

Future/Active на схеме это статусы категорий Запланированная/Активная, а present/NOT present in GS представлена ли категория в Golden set.

Еще один вопрос, который важно разобрать что мы хотим делать с Запланированными категориями? И что мы хотим делать с их детьми?

Есть несколько вариантов. Мы можем:

  • Использовать эти категории в классификации.

  • Не классифицировать и выбросить категории из GS.

  • Переразмечать эти категории в категорию-родителя.

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

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

  1. Убрать удаленные, редко встречающиеся (меньше 10 товаров в golden set) и те категории, у которых в названии есть свалка.

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

  3. Смаппить запланированные категории в sibling-категорию с другое/другие в названии, если такая есть.

  4. Удалить из Golden Set категории, у которых есть категории-потомки с товарами в Golden Set. Здесь происходит то самое разрешение конфликтов.

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

Валидация модели

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

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

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

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

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

'errors' - sum of errors of confusing two labels,
'label_1_confuse' - count(true=label_1, pred=label_2) / 'errors',
'label_2_confuse' - count(true=label_2, pred=label_1) / 'errors',
'fraction_of_error_to_label_1' - count(true=label_1, pred=label_2) / total_label_1,
'fraction_of_error_to_label_2' - count(true=label_2, pred=label_1) / total_label_2,
'fraction_of_all_errors' - 'errors' / total_errors,
'fraction_of_all_errors_cumulative'

Выводы

  1. Для удобной итеративной работы с деревом категорий полезно ввести статусы категорий. Такие статусы позволят обрабатывать категории в разном состоянии разными способами.

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

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

Подробнее..

Анонс онлайн-митапа по .NET три доклада о кроссплатформенных десктопных приложениях

24.11.2020 08:05:01 | Автор: admin

Вы уже не ждали, а мы сделали. В этот четверг, спустя почти год после предыдущего митапа, мы проводим первую (надеюсь, среди многих ?) онлайн-встречу с тремя бомбическими докладами про кроссплатформенные десктопные приложения. Миша Романов рассмотрит всевозможные фреймворки для кроссплатформенных приложений и расскажет, как и зачем. Никита Цуканов потравит байки про разработку Avalonia интероп с COM и автогенерированный CQRS прилагаются. А Саша Якунин расскажет о разработке библиотеки для отслеживания изменений в данных, которая позволяет делать real-time UI (как и другие штуки) действительно быстрыми.

Когда:26 ноября в 17:00 (Мск)
Где:Ютуб-канал Контура

Обзор кроссплатформенного GUI для.NET Миша Романов, разработчик в Контуре

.Net Core поставил знак равенства между словами .NETи кросплатформенно, но только для консоли и веба. А как же десктоп? В своем докладе Миша сделает обзор нескольких кроссплатформенных GUI-фреймворков, доступных.NET-разработчикам: от широко известных (таких, как Avalonia и GTK#) до малознакомых, но интересных (например, Eto.Forms).

Как добавляются новые фичи в Avalonia UI Никита Цуканов, основатель Avalonia UI O

На какие жертвы приходится идти, чтобы сделать UI действительно кроссплатформенным?

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

Эффективное кэширование или как получить максимальный RPS и real-time UI Саша Якунин, CTO в ServiceTitan

Саша автор библиотеки Fusion. Он расскажет вам, как относительно простая абстракция поможет серьезно повысить производительность веб-сервисов и сделать весь UI вашего приложения обновляемым в реальном времени. Разберёмся, как все это связано с Blazor, two hard things in computer science, eventual consistency, новизной React, и другими, казалось бы, несвязанными, проблемами.

До встречи! Жмите колокольчик на Ютубе, готовьте горячительные напитки, регистрируйтесь и подключайтесь к онлайн-митапу Kontur Tech Talks по .NET.

Подробнее..

ICFP Contest 2020 от идеи до воплощения. Как организовать контест и выжить

09.09.2020 20:04:18 | Автор: admin


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


Как все начиналось


Леша Кирпичников (beevee):


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

Последние два года мы писали организаторам, что хотим попробовать себя в новой роли и разработать следующее соревнование. В сентябре 2019 получили положительный ответ и предложение стать организаторами ICFPC 2020. Мы обрадовались, согласились и только потом задумались, куда именно мы вляпались :)


Про цель


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


Когда ICFPC только появился, 22 года назад, соревнование для программистов было редким явлением. Сейчас по миру проходит очень много разных соревнований, в такой реальности непонятно, какое место занимает ICFPC. Наш ответ такой: у всех остальных соревнований есть вполне определенный формат, а ICFPC это единственное место, где этот формат можно как угодно нарушить и делать максимально нестандартную штуку. Хочешь ботов писать иди на Coding Game и Russian AI Cup, хочешь алгоритмы на codeforces, а хочешь что-то необычное, свежее, непохожее ни на что на ICFPC.


Забегая вперед, скажем, что свою главную цель мы выполнили. ICFPC 2020 действительно стал необычным и запоминающимся. За счет чего это получилось и что нового было в этом году?


Максимально продуманная легенда


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


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


Зачем этот турнир? Что будет с проигравшими? Какой приз ждет победителей? Мы не знаем! Но наверняка лучше выиграть, чем проиграть :) Поэтому за 72 часа наша задача выбрать самый лучший алгоритм, который способны сделать земляне, и отправить в финальную битву именно его. Финал в ICFP Contest это и есть наш земной турнир между лучшими программистами со всей Земли за право поучаствовать в инопланетном соревновании от имени всех землян.


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


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


Формулировка задачи, Иван Зайцев и Пеговка


Часто в ICFPC громоздкая формулировка задач. Однажды было 40 страниц спецификации, и мы ее читали 4 часа. Это нас деморализовало. И у нас появилась идея: что если сделать контест без спецификации вообще? Вместо четких правил будут инопланетные сообщения, которые участникам нужно будет разгадывать самостоятельно. Когда придумывали сценарий, было очевидно, что сообщения из космоса мы должны как-то получить.


Так родился образ астронома Ивана Зайцева, который принимает на свой радиотелескоп в Пеговской обсерватории сообщения от инопланетян и просит помощи в их расшифровке. Это все стало первым вводным видео на ломаном английском



Для него Леша Кирпичников отрастил бороду (шутка, она сама отросла за время карантина), нашел медицинский халат, принес перфокарты. Мы распечатали текст, который Леша читает, чтобы он был как бы написан на перфокарте. Сделали бейдж с именем Ивана Зайцева, работника Пеговской обсерватории. В пользу легенды также создали блог Ивана Зайцева


Запись первого видео
Запись того, как Леша читает текст с перфокарты


Реквизит
Перфокарта с текстом, бейджик Пеговской обсерватории и карандаш


Кроме этого видео, были посты в блог и еще два вводных видео.


В общем, всеми силами старались создать логичную историю. Это было максимально нестандартно. Никто так не делал раньше. Поэтому в самом начале контеста народ активно спрашивал: Что делать то? Где правила, инструкции, какой код писать? Как очки зарабатываются?. На что мы отвечали Ребята, какие еще очки! Мы получили сообщение от ИНОПЛАНЕТЯН, помогайте нам его расшифровать быстрее!. Все разгадки участникам нужно было закидывать в чат Discord, а мы собирали все дельные идеи в публично доступную спецификацию. Коллективный разум разгадывает в 10 раз быстрее, чем отдельные люди. Спецификация к задаче появилась через 4,5 часа, и вся она состояла из цитат участников в чате.


Почему Иван Зайцев


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


  1. Есть канадский астроном Yvan Dutil. Он один из авторов сообщения, которое в 1999 и 2003 гг. земляне отправляли к звездам с помощью радиотелескопа в надежде найти там разумную жизнь.
    Многие, наверное, знают о существовании SETI института поиска внеземного разума. Но есть еще и менее известный институт METI, который изучает возможности не просто наблюдения за космосом, а общения с внеземными цивилизациями. И с Земли уходило в космос уже несколько десятков посланий. Несколько из них имели вполне профессионально составленное содержание. Смотрите Arecibo message (1974) и CosmicCall (1999 и 2003). В нашем сеттинге мы находимся по другую сторону, мы получатели подобного сообщения.
    Тут есть интересная проблема, как составить сообщение и суметь в нем рассказать что-то нетривиальное существу, восприятие которого, может быть, сильно отличается от нашего. Они не знают, где верх и низ, не знают базовых понятий, с помощью которых можно это донести. Yvan Dutil один из авторов сообщения, которое реально отправили в космос.
  2. Александр Зайцев человек, фамилию которого мы взяли.
    Это российский астроном, радиофизик, глава российской организации SETI. Он один из идеологов и активистов, благодаря которым как раз и реализовались проекты CosmicCall и CosmicCall2.

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


Из чата организаторов


Почему Пеговка


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


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


Итого задача участников была такая


  1. Расшифровать инопланетное послание.
  2. Понять, что там описывается язык программирования и виртуальная машина, его выполняющая, с дисплеем и тачскрином :)
  3. Реализовать на своем любимом языке программирования виртуальную машину и выполнить на ней последнее самое большое сообщение от инопланетного корабля.
  4. В запустившейся программе (участники назвали ее galaxy из-за значка, которым она обозначалась у инопланетян) разобраться с особенностями инопланетного пользовательского интерфейса, понять, что речь идет о галактическом турнире.
  5. Найти обучающие туториалы и изучить с их помощью правил игры.
  6. Написать бота, который будет играть в игру и победит всех.

Как придумывали концепцию задачи


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


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


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


Паша Егоров (xoposhiy):


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

Так, еще в 1960 году, когда человечество было уверено, что в ближайшие лет 5 установит радиосвязь с марсианами, немецкий математик опубликовал книгу Lincos. Проектирование языка для межпланетного общения. Часть 1. Несколько сотен страниц подробно описывают, как общаться с инопланетянами. Начать в этой книге предлагается с математики и логики, а далее на примере диалогов двух собеседников вводить более сложные понятия, например, хорошо и плохо.

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

Паша Егоров взялся за эту идею и начал ее прорабатывать. Он приносил на встречи такие картинки:



Мы их весело разгадывали, и через какое-то время стало понятно, что это самая проработанная часть. К ноябрю 2019 у нас был прототип сообщений от инопланетян, в частности описание виртуальной машины (на основе SKI-комбинаторов). Мы определились, что после введения этого языка программирования мы дадим участникам большое сообщение, для расшифровки которого им надо будет написать виртуальную машину, исполняющую код на инопланетном языке. После исполнения кода перед глазами участников должен появиться тачевый интерфейс инопланетной операционной системы (мы его называли aPad как iPad, только aPad от alien). А вот что будет внутри этой операционки, до последнего оставалось загадкой. У нас были готовы авторская реализация виртуальной машины и несколько примеров программ, которые можно было на ней запустить.


Турнир и космические битвы


В декабре 2019 мы активно обсуждали содержание операционки. Победившей была идея PlanetWars. Бои кораблей, которые вращаются вокруг планеты в двумерном пространстве. Первые картинки этого выглядели так:



Идея требовала работы с числами с плавающей точкой. Это была проблема вводить на инопланетном языке стандарт IEEE 754 никому не хотелось. Но тогда на нас накатило вдохновение, полученное от решения задачек из 12 дня Advent Of Code, которые как раз тогда мы все решали: гравитация должна быть дискретной. Долгое время эта часть контеста так и называлась у нас с префиксом АoC в честь Advent of Code, который подарил нам идею.



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



(траектории на картинке декоративные, в пиксельном отображении их не было)


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


Инопланетный интерфейс


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


Саша Храмцов:


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

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




(история галактических боев с просмотром каждого боя)


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


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


(интерфейс космического боя и управления кораблем)


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


Наша версия:


Одна из версий участников:

(это главный экран операционной системы, центр галактики точка (0, 0))


Саша Храмцов:


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


Во время контеста мы собирали скриншоты, которые участники публиковали в чате, твиттере и инстаграме получился целый альбом!



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



Про перевернутые картинки


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


Из чата участников:


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


Предлагаем вам трехминутную экскурсию по интерфейсу Галактики:



Инфраструктура


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


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


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


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


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


Никита Сивухин:


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

Про 72 часа контеста на стороне организаторов


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


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



(Организаторы контеста за работой)


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


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



(Иван Зайцев смотрит трансляцию и переживает за судьбу землян)



(кадр из трансляции: можно было подглядеть крутые стратегии. На картнке корабль защитника разделился на много-много маленьких корабликов и их траектории выглядят запутанным клубком)


Демо боя можно посмотреть в отчете победителей


Про зрелищность и атмосферу


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


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


Атмосфера соревнования была скорее дружеской: все обсуждали идеи, общались, подшучивали друг над другом. Например, мы изменили название постоянного участника ICFPC команды The Cat #1. Написали специальный код, который в лидерборде переименовывал название команды на текущее место, которое они занимают, например, место 12 и название становится The Cat #12. Это просуществовало часов 6, прежде чем ребята из команды написали в чат: Эй, вы чего сделали название нашей команды динамическим?))) Вы клевые, нам понравилось!.


Каким получился для вас этот контест


Вероника Самохина(aminopyrodin): Эпично сложным и необычным.
Леша Кирпичников: Мы делали контест, в который понравилось бы играть воображаемой идеализированной версии нашей команды. Команде из 5 или больше промышленных разработчиков, которые готовы почти не спать и рубиться все 72 часа, но при этом команде из гораздо более умных и крутых участников, чем мы сами. Это и хорошо, и плохо: нам бы точно понравилось в это играть, но были и команды с другими ценностями, которым это совсем не зашло. В конечном итоге по-другому и быть не могло: вряд ли мы бы стали делать что-то, что не понравилось бы нам самим.
Саша Храмцов: Максимально мозголомный, эпический и масштабный. Потому что история, которая за всем этим лежала, развернулась очень широко. Получилась настоящая история. Контест из виртуального превратился в настоящий.
Никита Сивухин: Многогранный и продуманный. Было очень много точек взаимодействия и компонентов системы как снаружи, так и внутри.


Про планы


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




Победители


Main round, 1st place


Первое место в основном раунде получила команда Closed and Restricted Boltzmann Machine (denplusplus). Ребята лучше всех выполнили задание, писали на Python, играли под нейтральным флагом, потому что все выходцы из России, но живут и работают в разных местах западного побережья США.


Main round, 2nd place


Второе место заняли две команды из Японии: многократные победители ICFPC Unagi, которые писали на Rust, и команда японских студентов 182020, их язык программирования C++. Эти команды обе заняли одно место, потому что их алгоритмы хоть и были разными, но показывали практически одинаковую результативность в турнире: то одна, то другая команда выходила вперед. Мы не смогли их разделить по разным местам и дали обеим командам второе место.


Lightning Round


Команда powder победитель lightning раунда. Этот раунд длится первые 24 часа: выигрывает та команда, которая добилась наибольшего прогресса за это время. В нашем случае они решили больше всех туториалов, обучающих, как вести космические бои. В составе команды четыре человека из разных стран. Ребята писали на языке Haskell.


Judges Prize


Приз жюри получила команда ребят из Уфы WILD BASHKORT MAGES (ripatti). Они так увлеклись написанием виртуальной машины, запускающей галактику, что решили реализовать своих ботов для игры так, что они играли без реверс-инжениринга протокола игры (как делали большинство), а нажимая на экран галактики и распознавая изображения. Неэффективно, но впечатляюще. Использовали язык OCaml.


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


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

Что почитать дальше


Пройти путь участника соревнования и попробовать расшифровать инопланетные сообщения


Начать нужно вот с этой страницы. Скачать recording of this message и попытаться понять, что же имелось в виду.


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


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


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


Почитать книгу про реализацию функциональных языков программирования


Главная книга, которая помогала нам делать контест, это The Implementation of Functional Programming Languages, которую совершенно бесплатно можно прочитать здесь


Буквально нескольких первых глав достаточно, чтобы достойно справиться с задачей, описанной в инопланетных сообщениях :)


Сразиться в космическом бою с другой командой


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


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


Вдохновиться и посмотреть пару хороших фильмов по теме


Подробнее..

Стираем границы на первой онлайн-конференции аналитиков

17.08.2020 12:08:41 | Автор: admin

image


У нас в Контуре большое сообщество аналитиков. Ребята регулярно проводят летучки, ходят друг к другу на стажировки и раз в год встречаются на командном выезде. И всё было хорошо, пока аналитиков не стало 150! Тогда мы решили, что формат ежегодного выезда устарел и придумали сделать свою конференцию. С самого начала мы хотели пригласить в гости инженеров из других компаний Екатеринбурга, но онлайн подарил нам возможность позвать вообще всех желающих.


Когда: 20 и 21 августа
Где: на Ютуб-канале Технологии в Контуре


У нас классные спикеры


Мы делаем конференцию для себя, а значит, выбираем только самые интересные материалы. Во-первых, нам самим есть, что рассказать: за год наши аналитики приняли десятки вызовов, накопили море опыта и кучу интересных историй. Во-вторых, аналитики в Контур приходят всерьёз и надолго и со временем плохо представляют себе процессы за его пределами. А ведь любопытно! Поэтому мы позвали в гости экспертов из четырёх классных компаний: Додо Пицца, Жизньмарт, Боксберри и ЦИТ.


Программа огонь!


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


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


Менеджерам и разработчикам тоже будет интересно


Наша конференция не только для аналитиков, но и для всех, кто с ними пересекается. Или наоборот. К примеру, Ксюша Лысенко из компании Жизньмарт расскажет про то, как они справляются и выстраивают процессы вообще без аналитиков. Кирилл Сурганов из ЦИТа поделится особенностями настройки всего цикла заказной промышленной разработки. А ребята из Додо Пиццы расскажут про выход на зарубежные рынки и порассуждают, кто главный: бизнес или всё таки разработка?


Ну и заключительный призыв


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


Приходите поговорить о границах работы аналитиков и о том, как эти границы нарушать.

Подробнее..

Анонс онлайн-митапа по тестированию три доклада про плохие процессы в команде, хотфиксы и первые шаги в автоматизации

07.07.2020 10:10:24 | Автор: admin

image


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


Когда: 9 июля в 16:00 (Мск)
Где: Ютуб-канал Контура


Менять процессы нельзя страдать Катя Синько, Контур


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


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


Качество релизов ответственность команды Люда Малеева, Miro


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


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


5 открытий: дневники автоматизатора Инна Шундеева, Контур


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


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


До встречи! Жмите колокольчик на Ютубе, готовьте прохладительные напитки, подключайтесь и смотрите онлайн-митап Kontur Tech Talks по тестированию.

Подробнее..

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

05.03.2021 08:07:29 | Автор: admin

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

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

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

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

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

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

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

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

Задача в тестовом была сложной. Ходила шутка (или не шутка), что тот, кто решит тестовое, может сразу трудоустраиваться джуниором, а не стажером.

Долго так работать не получится. И у нас не получилось.

Больше голов нет

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

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

Все сошлось нужно было переделывать.

Подопытные проекты

Раскатывать изменения сразу на стажировку не стали, начали экспериментировать на проектах поменьше и стартанули со Школы промышленной разработки (Шпора). Там проблемы с тестовым были почти такие же, как у стажировки:

  1. Слишком сложно для студентов 2-3 курсов.

  2. Студенту трудно подступиться к такому большому заданию.

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

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

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

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

  1. 70 решенных тестовых и 138 тестовых с хотя бы одной задачей вместо 63, решенных в предыдущем году.

Мы признали эксперимент успешным и перешли к изменению тестового на стажировку.

Что и как оцениваем?

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

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

  1. Чистый код не врет. Чтение такого кода не превращается в детектив, где тебя пытаются ввести в заблуждение. Намерения чистого кода выражены в именах классов, методов и переменных и совпадают с фактическим поведением. Если метод называется GetNode, то он должен вернуть узел вызывающему коду, а не сложить его в поле класса. Как проверить себя? Уберите из кода тела всех методов. Можно ли по выжимке догадаться, как работает решение?

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

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

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

Добавляем контекста и боремся с формулировками

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

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

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

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

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

Тестовые проверяют 10 разработчиков. Каждый отвечает за проверку одной задачи во всех тестовых.

Что по метрикам

В 2019 году с одной большой задачей мы получили 55 тестовых. В 2020 году все шесть задач решили 63 человека, и ещё 281 кандидат решил хотя бы одну задачу. В 2019 году мы проверяли тестовые больше месяца, в 2020 году на это ушло две недели.

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

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

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

Хотите проверить формулировки (ну и посмотреть задачи, конечно)? Набор на стажировку открыт и тестовое лежит на Ulearn. Чтобы посмотреть тестовое и начать решать, вступите в группу.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    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