Продолжаем серию статей про тех, кто делает проекты своими руками. Со Степаном Гончаровым мы поговорили о том, как органично менять направления профессионально деятельности, и при этом из Android-разработчика переквалифицироваться в DevOps. Расспросили про релизный цикл и процессы в Grab — компании, в которой только под Android разрабатывают 40 человек. Порассуждали про то, откуда черпать идеи для игр, расспросили про OpenSource-проекты Степана Archetype и kOptional.
О госте: Степан Гончаров (stepango) разрабатывает приложения и игры под Android c 2008 года, c тех незапамятных времен, когда Android SDK не вышел. За свою карьеру успел побывать в роли QA, менеджера, маркетолога, блогера, аналитика, советника и многих других. Участвовал в разработке как никому неизвестных приложений, так и приложений с миллионами пользователей по всему миру. В настоящее время работает в Grab, активно использует Kotlin и Rx и все больше времени посвящает OSS.
Перед вами текстовая расшифровка подкаста Run Loop. Ведущие: Илья Царев, Алексей Миляев и Роман Бусыгин.
Илья: Расскажи, чем непосредственно занимается компания Grab и какая твоя роль. Ты просто пишешь код или что-то больше?
Степан: Изначально Grab — это райдхейлинг-сервис. Можно заказать себе такси, к тебе приедут, заберут, отвезут. Но в последнее время компания все больше обрастает дополнительными сервисами вроде доставки еды и своей системы платежей. Когда я пришел в Grab, я руководил командой разработки драйверского приложения. Потом переключился на пассажирское приложение. Одним из моих последних проектов был полный редизайн пассажирского приложения. Сейчас я занимаюсь больше DevOps — настройка CI, оптимизация времени сборки и всё такое.
DevOps
Алексей: Степан, а если не секрет, почему ты решил переключиться именно в DevOps и CI? Я часто слышу такую историю, когда мобильные разработчики приходят в платформу, полны энтузиазма, прокачиваются до невероятных высот, а потом решают вдруг пойти на какие-то непродуктовые задачи.
Степан: На самом деле всё получилось, я бы сказал, органично. Во время того, как я занимался редизайном приложения, у нас была экстренная миграция на GitLub из-за того, что инстансы CircleCI уже не могли собирать наш проект. Он оказался слишком большим и ел много памяти, а инстансов CI подходящих не было. Как-то так я на несколько недель застрял, пытаясь мигрировать наши процессы и вообще все, связанное с CI, на GitLub. Потом, после того как проект закончился, у нас сформировалась новая платформенная команда. Пока я провел 3 недели в отпуске, все другие задачи заняли, поэтому, когда я вернулся, мне сказали: "Всё, занимаешься оптимизацией скорости сборки и CI".
Разные роли и занятия
Илья: У нас тоже есть птички — не те, которые микрофоны-петлички, но все-таки на хвосте донесли, что ты побывал и QA, и менеджером, и маркетологом, и блогером, и аналитиком — в общем, прошел через множество разных занятий. Расскажи, пожалуйста, подробнее, как тебя во все эти роли заносило, долго ли ты в них находился, что тебе в каждой, может быть, нравилось, не нравилось? Может быть, ты сейчас бы с радостью хотел обратно в блогеры?
Степан: В принципе, в каждой из этих ролей как-то так органично получалось плавно перетекать из одной в другую. Все началось с того, что я заинтересовался Android-разработкой еще до выхода первого девайса на Android. Меня заинтересовала сама идея — операционная система на Java — как-то необычно. В итоге, во время учебы в университете я, вместо того чтобы делать лабораторные работы на C или другие скучные университетские задачки, все плавненько сводил к Android. Преподаватель был не против, ему тоже было интересно что-то новое узнать, и я писал Android-приложения.
К тому времени, как я закончил бакалавриат, у меня уже был какой-то опыт. Я написал свое первое приложение, выпустил его в Маркет, и с этого началось увлечение маркетингом. У меня было свое приложение, и мне надо было как-то его продвигать. Я начал немножко баловаться и писать блоги.
В 2010 я нашел работу Android-разработчика, тогда это было практически нереально — рынка никакого не было, про Android никто не знал. Я попал в аутсорсинговую компанию, и мне пришлось набирать себе команду, имея за плечами всего полгода опыта и 2 опубликованных приложения.
Таким образом, из маркетинга и блогерства я вернулся обратно в разработку. Потом закончил магистратуру, переехал в Питер — тоже в аутсорс. Там я начал увлекаться автоматизированным тестированием, и так вышло, что я развивал направление Robotium в компании и обучал QA писать автоматизированные тесты, пока не уехал в Сингапур, где опять же набирал свою команду в один из Сингапурских стартапов. Уже после этого начал дальше развиваться в Android-разработке. Теперь вот попал в Grab — сначала в менеджеры, потом в DevOps.
Роман: Степан, скажи, пожалуйста, ты не скучаешь по этому времени, когда у тебя задачи достаточно кардинально менялись? Сейчас ты, грубо говоря, каждый день пилишь DevOps — и все?
Степан: На самом деле, пока нет. Я недавно закончил большой проект, и в принципе, пока что задач хватает. Они могут казаться, конечно, одинаковыми — то есть цель одна, но то, чем я занимаюсь, это много-много разных вещей, в том числе, например, memory-profiling, работа с CI. Сейчас посматриваю на Amazon-аналитику по S3. Это все для меня пока новое. Я не думаю, что я долго буду заниматься именно CI и оптимизацией времени сборки. Скорее всего, например, в следующем квартале я переключусь на что-то другое.
Роман: У меня, например, цикл перехода от одного к другому направлению примерно года 2-2,5. Я начинал с тестирования, потом переключился в нагрузочное тестирование, потом стал разработчиком. Как часто у тебя происходит смена интересов и направлений в работе?
Степан: Где-то в районе 1,5-2 лет. Но последнее время получалось по полгода.
Игры
Алексей: Чем бы тебе больше всего хотелось заниматься? В идеале, может быть, ты вообще хотел бы уйти из разработки, не трогать ничего руками? Или, может быть, ты, наоборот, хотел бы закопаться в какие-то глубокие нативные штуки? Чего тебе конкретно больше всего хочется?
Степан: Это хороший вопрос! Я на самом деле так и не решил. Мне все еще интересно довольно много вещей. Одна из самых для меня интересных, куда мне не получилось попасть, — это разработка игр. Во время учебы в университете я увлекался написанием всяких игр. У меня были проекты на Flash. Второе мое приложение под Android тоже была игра. Я даже месяц работал в игровой компании, но как-то у меня не зашло.
В принципе, я бы хотел попробовать именно игровой дизайн, но с этим пока сложновато. Чем дольше я трачу время и развиваюсь в Android, тем сложнее будет этот переход, если он вообще случится.
Илья: Стёпа, если мы затронули тему игр, то вопрос такой — о чем была твоя первая игра?
Степан: Это был 2010 год, поэтому она была довольно простая. Это была логическая головоломка с лазерами и зеркалами, которые надо было поворачивать таким образом, чтобы лазеры попали в цель. К сожалению, эта игра хостилась на аккаунте тогдашнего моего работодателя. Этот аккаунт уже давно удалился и игры там нет. Но мне она очень нравилась. Написана она была полностью на Android View, о чем я, конечно, жалею, но опыт был отличный.
Илья: А сейчас ты находишь время, чтобы заниматься разработкой игр?
Степан: Нет. Абсолютно не хватает времени на игры сейчас. Помимо того, что я работаю в компании, я еще организую Kotlin-митапы в Сингапуре и выступаю на конференциях. То есть свободное время полностью забито. Может быть, когда-нибудь и вернусь к этому. Купил недавно Гугловский VR попробовать Unity, когда будет время, но пока что не удалось.
Роман: А скажи, пожалуйста, откуда ты черпал идеи для игр? Как они приходили к тебе в голову?
Степан: Идеи для игр обычно приходят ко мне из технических новинок. Я обычно смотрел какие-то технологические демки, либо, например, в 2008-2010 была мода на физические движки. Видишь какую-то клевую технологию или новый модный шейдер, и идеи сами появляются исходя из того, что ты видишь.
Роман: Связано ли твое хобби делать игры с тем, что ты сам геймер — рубишься в PlayStation, в Switch, на PC — есть ли какая-то корреляция?
Степан: Да, я думаю, что есть. В детстве у меня была Sega, потом PC, довольно много времени потратил, играя в игры. Сейчас у меня есть и PlayStation, и Switch, но сейчас уже гораздо меньше времени трачу на игры, не хватает его даже поиграть.
Роман: А какие игры тебе больше всего запомнились? Или какой жанр игр тебе больше всего нравится? Мне, например, больше нравятся шутеры или какие-нибудь Survivor-хорроры.
Степан: С шутерами у меня как-то не сложилось. Я играл в Counter-Strike в детстве, но не затянуло. Одна из любимых игр — это Космические Ренджеры-2, и первая часть тоже эпичная, конечно. Потом из запоминающихся это был Freelancer.
Алексей: Freelancer — это игра? Это стиль жизни!
Степан: На самом деле, это космический симулятор, грубо говоря, очень близкий к Космическим Рейнджерам, но от третьего лица. Еще Ведьмак, Arcanum, и Fallout — в таком духе.
Илья: Мне кажется, что разрабатывать игры — это мечта большинства программистов. Как мне всегда казалось, люди вообще идут в разработку только для того, чтобы делать игры. Это же супер-классно! Мой первый проект на iOS — это тоже была игра, кстати. Вы что думаете про это?
Алексей: Я слышал, что у многих действительно путь в программирование начинается с того, что они хотят сделать какую-нибудь игру, им нравится играть в игры, они хотят понять, как это делать. У меня почему-то такого не было. Меня больше интересовало, как работают программы — что происходит, когда ты кликаешь на окошечки в Windows. Но я понял в какой-то момент, что совсем не представляю, как делать игры. Я понимаю, как можно написать то или иное приложение на мобилку, ту или иную программу на ПК — как делать игры я не представлял. Как-то меня так занесло на канал на YouTube. Там чувак на Java и Canvas с нуля писал свою игрушку — клон какой-то RPG-игры чем-то похожей на помесь Diablo и Final Fantasy. Это реально очень интересно. Я бы всем советовал просто попробовать в свободное время с этим поиграться, понять, как это вообще пишется, как оно устроено. Это был очень классный опыт. Дальше меня как-то занесло в такие же уроки, где чувак пилит на Unity какую-то простенькую игру. Очень интересно сравнить подходы, когда ты начинаешь писать свой движок, и когда у тебя уже есть что-то готовое типа Unity, и ты его как-то подстраиваешь, чтобы сделать свое. Я бы очень рекомендовал что-то такое попробовать, потому что это, правда, очень интересно.
Роман: У меня похожий опыт. Я люблю играть, но я не заядлый геймер, но при этом мне в первую очередь было интересно понять, как делаются программы. Потом я уже прихожу к тому, что то, что я люблю, что мне нравится, я хочу попробовать сделать своими руками. Но в плане игр у меня все начинается с идеи. Я жду идеи, которую можно будет реализовать в любое свободное время, либо расчистить свое время и сделать эту игру.
Степан: По поводу идей, одна из последних, которая мне приходила в голову, была связана с автоматической генерацией карт. Сейчас это, можно сказать, тренд, и некоторые игры используют генерацию огромных миров, чтобы они были очень похожи на настоящие.
Есть целый класс алгоритмов, которые позволяют генерировать ландшафт. Ландшафт можно строить так, что там будут и горы, и моря. В принципе, если человеку показать вид Google карт сверху и сгенерированный ландшафт, мало кто сможет их отличить. Это оченьинтересно, потому что можно единолично, не имея больших ресурсов, создавать для игры контент, потому что это одна из самых сложных частей. По крайней мере я считаю так.
Рабочий день
Алексей: Степан, как обычно проходит твой рабочий день? Ты проснулся, добрался до офиса, или не добрался — что ты делаешь, как ты делаешь?
Роман: Мне еще интересно, что у тебя происходит до того, как ты добираешься до офиса? Может быть, у тебя есть какие-то любимые кофейни или любимые традиции: например, ты любишь прогуляться по скверику перед офисом? С чего реально начинается твой день?
Степан: В последнее время мой день начинается с 50 отжиманий.
Алексей: Уважуха!
Степан: Я в какой-то момент понял, что надо больше времени уделять физической активности, и сейчас стараюсь в свободное время заставлять себя это делать. В принципе, ничего особенного.
Так как основной бизнес компании — это райдхейлинг, то есть заказ такси, мне компания оплачивает такси. Поэтому первое, что я делаю утром после завтрака, это заказываю такси и еду на работу.
Работаю я в довольно приличном офисе с классным видом на залив. Как, наверное, у большинства из нас, первое дело — это, конечно, чашечка кофе. Чаще всего мы просто с коллегами спускаемся в подвал этого же здания, заходим в одну из кофеен, берем кофе и идем обсуждать какие-то новости, планы. Особенно интересно, если за эту ночь, которая в США — день, что-то происходит: что-то новое зарелизили, какие-то интересные новости, новый фреймворк. Все это мы обсуждаем, и после этого можно начать работать.
Дальше все примерно, как у всех: заходишь в Jira, смотришь тикеты. Последние пару недель я выступал в роли on-call. Это такой разработчик, на которого сваливаются все продакшен-проблемы, и ты должен как-то решить, или делегировать, или сказать — это не проблема и забыть про нее.
Так как команда очень большая (над пассажирским приложением работает уже больше 40 человек — это разные команды, которые отвечают за разные области), иногда бывает довольно сложно найти нужного человека, который должен эту проблему решить. У нас есть такая ротация — каждую неделю один из разработчиков занимается разгребанием продакшен-проблем. Конкретно эту неделю я занимаюсь подготовкой релиза, это исправление issue из тестирования, и опять же нахождение правильных людей, чтобы это быстрее всего решить.
Роман: 40 человек — это только Android, или это вся команда, которая делает мобильное приложение?
Степан: Это только Android.
Роман: Вау! Когда я услышал, сколько мобильных разработчиков в Uber, мне стало не по себе. Но ваша команда подтверждает этот тренд. Ты упомянул, что ты был, грубо говоря, дежурным по баг-репортам. Расскажи, как у вас проходит релизный цикл приложения?
Релизный цикл приложения
Степан: В принципе, я думаю, это более или менее обычный процесс, которого крупные приложения стараются придерживаться. У нас есть фиксированный Release Train. В данный момент это неделя. То есть каждую неделю во вторник у нас релиз. За эту неделю выбирается релиз-инженер, и к началу недели все фичи, которые падают в релиз, должны быть готовы.
Если какая-то фича не готова, ее выкидывают, закрывают флагом — делают что угодно, чтобы она не попала в регрессию. Как только этот билд, где все фичи готовы, отдают в QA, начинается процесс release-engineering. То есть любые открытые баги надо фиксить как можно быстрее, им отдается самый высокий приоритет. Даже если разработчик работает над следующей фичей, которая уйдет в следующий релиз, но есть баг или какая-то проблема, которую он знает лучше всего, и у него идет меньше всего времени ее решить, она отдается ему.
Это происходит у нас до пятницы. В пятницу мы стараемся закончить регрессию, все regression-баги закрыть, и уже со спокойной душой уйти на выходные. Если что-то внезапно случилось, в понедельник можно быстренько исправить, и уже в понедельник вечером или во вторник утром опубликовать и раскатать потихонечку — сначала 10%, потом больше, больше, больше. За неделю мы стараемся где-то к соточке подобраться, и уже следующий релиз релизить.
Тестирование и обновления
Роман: Быстро сочинить и выложить — а как же процесс тестирования? Специальные люди-тестировщики, которые проверяют, в какой момент это делают?
Степан: Тестировщиков много, они успевают. Я не знаю сколько их точно. Причина в том, что большинство команд находится не в Сингапуре. Не помню, сколько у нас офисов, но как минимум, еще 5 офисов есть, где команды разработки вместе с QA. Во время разработки фич эти команды вместе с QA сами отлаживают эти фичи, тестируют. К моменту начала релизного цикла фичи не должны иметь открытых багов. Регрессия происходит так: выделяется один, может, два QA от всех команд, и они прогоняют регрессию по их фичам. Как раз за эту неделю, пока ищутся баги, регрессии фиксятся.
Илья: Получается, что у вас недельный цикл обновления, а пользователи успевают за такое время обновится? Мы, например, сталкивались с ситуацией, что у нас iOS-пользователи успевают обновиться, а Android — не особо. У нас прямо на несколько недель отличались версии.
Алексей: Да, мне тоже кажется, что неделя как-то быстро по ощущениям.
Степан: Да, так и есть. Неделя — довольно быстро, но у нас нет цели, чтобы все пользователи обновились. Мы перешли с двухнедельного релизного цикла на недельный для того, чтобы уменьшить нагрузку на QA во время регрессии, потому что за 2 недели накапливалось огромное количество фич. 40 человек могут наделать делов! Потом уже, под конец второй недели нам, бывало, вылезали довольно серьезные баги, которые получались из-за наложения этих фич, и их было довольно сложно решать. Получается, мы разбили понедельно и распределили эту нагрузку по тестированию более равномерно.
Илья: Сколько примерно процентов от вашей базы пользователей пользуются самой свежей версией?
Степан: Смотря про какой временной промежуток мы говорим.
Илья: В моменте.
Степан: Самой свежей версией ты можешь пользоваться, только если ты попал в этот reload-план на неделю. То есть 100% reload будет либо в районе пятницы, либо уже в понедельник, так как уже во вторник новая версия выходит. Тут уже зависит от удачи. Но у нас нет такой цели, чтобы пользователи всегда сидели на самой последней версии. Единственное, о чем нам еще нужно подумать — это минимальная поддерживаемая версия. Мы периодически заставляем пользователей насильно обновиться, когда, как минимум, 90% пользователей будет на минимально поддерживаемой версии, это примерно 3-4 месяца.
Организация командной работы
Алексей: Степа, у меня такой вопрос. Ты говоришь, что 40 человек занимается Android — правильно? Это прямо серьезно 40 разработчиков что-то делают?
Степан: Да.
Алексей: Есть такой момент, что с ростом количества сотрудников, особенно в одной области компании, то есть конкретно в Android-разработке, затраты на коммуникацию растут по экспоненте. Все бизнес-процессы занимаются тем, что разруливают это явление. А как это разруливается у вас? Какая организационная структура у этой машины из 40 разработчиков?
Степан: На самом деле решение у нас довольно простое. Эти 40 разработчиков не занимаются одним и тем же, и им по сути не надо сильно коммуницировать друг с другом. Получается, что команды в разных странах занимаются разными фичами. В принципе, тебе не нужно общаться с другими командами. Ты общаешься в основном со своим менеджером. В итоге затраты на коммуникацию не такие уж и большие, потому что команда — это 5-6 человек. Бывают и команды из 1-2: например, только какая-то новая бизнес-вертикаль появилась, там один разработчик, и у него просто engineering head, который отвечает за все, включая бэкенд, iOS и остальные вещи.
Получается, что у нас просто маленькие команды, которые работают в одном репозитории, в одной кодовой базе.
OpenSource-проекты Archetype и kOptional
Роман: Предлагаю перейти к следующему вопросу. Я узнал, что у тебя есть OpenSource -проекты. У меня всегда вызывает уважение, как разработчики умудряются находить время и на OpenSource, честный с пользователями и т.д. Расскажи, пожалуйста, о двух своих популярных проектах — это Archetype и kOptional. Что это такое?
Степан: Начну с kOptional. Из названия понятно, что это небольшая реализация Optional. как-то понадобилось очень срочно обрабатывать Null, и я запилил очень маленькую библиотеку. На самом деле там ничего особенного нет. Мне не хотелось сильно заморачиваться, я взял реализацию из Java8 и портировал.
Честно скажу, получилось так себе. Я, конечно, был доволен — это было лучше, гораздо лучше, чем ничего. Но если бы я тогда знал немножко лучше код, сделал бы по-другому.
Про Archetype — это довольно интересная история. Я где-то два года назад присоединился к стартапу, который называется 90 Seconds. Это одна из крупнейших в мире платформ для онлайн-производства видео: ты туда приходишь, как заказчик, описываешь свою идею, и тебе делают видео. С другой стороны, это платформа для исполнителей: у тебя есть видеокамера, ты умеешь снимать — регистрируешься и тебе приходят заказы.
Я начал писать приложение с нуля, и в какой-то момент нам понадобился второй разработчик. Тогда я познакомился с Денисом Неклюдовым и нанял его. Он приехал к нам в Сингапур. Когда он увидел, что я натворил, то говорит: "Давай сделаем из этого доклад". Почему нет?
В итоге доклад — хорошо, но нам понадобился какой-то пример кода, чтобы людям можно было показать. Естественно, код 90 Seconds уже не зашаришь никуда, там NDA и все такое. Буквально за несколько дней перед конференцией мы накидали этот проектик, и с тех пор он выполняет роль тестового задания, чтобы пройти собеседование в 90 Seconds: надо законтрибьютить, добавить какую-то маленькую фичу, что-то сделать в этом проекте.
В принципе, это такой шоу-кейс архитектуры, которую я использовал при написании 90 Seconds. Тогда это было и для меня в новинку, и, в принципе, для разработчиков, с которым я был знаком. Тогда, буквально два года назад, я начал писать приложение полностью с нуля, и решил все сделать изначально на Rx. Идея была в том, чтобы абсолютно все сделать реактивно, и абсолютно все на Kotlin, с нуля без всякого legacy.
Получилось довольно классно, за исключением пары вещей, о которых я до сих пор сожалею, но… вот так вот.
Алексей: Слушай, мне очень понравилась идея перед собеседованием просить человека законтрибьютить в твой OpenSource-проект. Это и проект развивает, и ты можешь посмотреть, как человек кодит, и вообще, что он думает, какие фичи предлагает. Мне кажется, нам надо это попробовать обязательно!
Давай немножко разовьем тему конференций. Ты ведь уже на следующей неделе выступаешь на AppsConf. Расскажи, пожалуйста, про свою тему и про что ты будешь там рассказывать?
Конференция AppsConf
Степан: Да, выступаю на AppsConf. Тема довольно интересно звучит на английском, но при переводе на русский мне не нравится, но ничего лучше не могу придумать: «Архитектура слоя исполнения асинхронных задач». Этот доклад я планировал, наверное, года три уже сделать, и все никак руки не доходили.
Идея появилась как раз из реализации, которую мы с моими коллегами использовали в приложении стартапа, куда изначально я приехал работать в Сингапур. У нас была довольно сложная задача, это была социальная сеть для музыкантов, в которой можно было писать музыку. Одна из фич была, что ты можешь писать музыку в офлайне, и потом, когда ты выйдешь обратно в интернет, все засинхронизируется с сервером.
При этом структура была довольно сложная. Сначала есть такая сущность, как идея песни, а потом в ты можешь преобразовать эту идею в дерево. По сути, получается контроль версий для твоей песни. Сама песня тоже была — мультитрек, эффекты и еще куча всего.
Получался такой граф зависимых сущностей, который надо синхронизировать. Мы написали такую интересную систему, которая, во-первых, оборачивала все эти запросы, во-вторых, позволяла их потом записать в базу, соединить с базой и отправить.
Мне это тогда показалось очень классно, и с тех пор я хотел об этом рассказать. Но, к сожалению, мне уже сейчас с опытом кажется, что реализация была просто ужасная. Буквально после того выступления с Денисом мне предложили опять выступить на Mobius, и с тех пор я этот доклад готовил. Доклад вообще трансформировался довольно долго.
В итоге получился такой шоу-кейс, как можно, придерживаясь очень простых правил, спроектировать красиво и элегантно какой-то компонент, который поможет избежать копипаста во всем проекте, и при этом позволит создавать что-то похожее на WorkManager, но даже более сложный.
Доклад будет про то, как пользоваться Kotlin, как проектировать компоненты приложения, и как можно эффективно управлять задачами, при этом решая какие-то очень распространенные проблемы очень простым путем.
Алексей: Скажи, этот опыт сейчас у вас используется в компании?
Степан: Нет, к сожалению. Во-первых, когда я пришел, там уже было довольно большое количество кода, и сейчас уже внедрить подобную систему довольно дорого, да и никому не нужно. Но, тем не менее, у меня есть план использовать похожие техники для сокращения количества кода, который мы используем.
Грубо говоря, одна из вещей, про которую я буду рассказывать — это как можно избавиться от, как я это называю, ручных подписок, когда ты просто внутри своих объектов вызываешь subscribe. Если просто делегировать вызов этого метода какому-то объекту, который ты будешь вызывать во всех местах, это может улучшить практически всю кодовую базу. Это я тоже хочу внедрить в наш проект.
Алексей: Завершающий короткий вопрос, ждет ли нас какое-то OpenSource решение для этой темы, которое поможет это внедрять в других компаниях?
Степан: Вряд ли. Я не планировал делать это решение в production-ready, но те сходники, которые будут у меня в презентации, будут зашарены на GitHub.
Роман:
Ох, заинтриговал! Всем тем, кто попал в ту же категорию заинтригованных людей, я напоминаю, что Степан будет на конференции AppsConf 8–9 октября в Москве.
AppsConf уже совсем скоро, расписание получилось очень насыщенным, в нем куча полезных докладов, и еще можно успеть забронировать билет и тоже принять участие.
Автор: Егор