Эволюция процесса разработки стартапа на примере команды Supl.biz

В прошлых публикациях мы делились опытом, как выстраивали в компании отдел продаж, как пробовали выходить на международный рынок, росли и привлекали инвестиции. Сегодня заглянем в историю развития компании с другой стороны – отдела разработки. Знакомьтесь, Олег Красиков – product manager. Олег расскажет, как развивался процесс разработки в Supl.biz. Наш опыт будет особенно полезен разработчикам, которые работают в растущих компаниях и тем, у кого процессы становятся сложнее.

В закладки

Привет, меня зовут Олег, я из компании Supl.biz. Supl.biz – это электронная торговая площадка для бизнеса. Мы помогаем одной части наших пользователей находить поставщиков необходимых им услуг, а другой – находить новых клиентов. Обычно, поиск поставщиков – достаточно трудоемкая задача, потому что процесс поиска выглядит примерно так: мы ищем их телефоны и начинаем звонить, часто попадаем не туда или к продавцу, у которого очень дорого. На это может уйти большое количество времени. У нас же для создания заказа покупателю необходимо потратить не более 1 минуты, а поставщику товара столько же, чтобы на него ответить. В результате, покупатель может сэкономить, в среднем, до 20% стоимости закупки, а продавец – получить дешевых лидов. За 2018 год на площадке размещено заказов на общую сумму 42 млрд руб.

Развитие процессов разработки

Нашим сервисом ежемесячно пользуется 150 тысяч компаний России и СНГ, которые размещают 15 000 заказов и в три раза больше предложений в месяц. Чтобы сервис развивался и был доступен пользователям 24 часа в сутки, у нас собственный отдел разработки из 10 человек. В этой статье хочу рассказать, как развивался процесс в отделе, с какими проблемами масштабирования компании столкнулись и как работаем сейчас.

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

Площадка запустилась в 2013 году, с 2015 года начали масштабироваться и взяли в штат двух разработчиков. Настроили CI/CD, тестовый сервер, проект был маленький и отлично работал, даже несмотря на прямые пуши в мастер и отсутствие закрепленного процесса работы с задачей. Процесс разработки представлял собой следующее: CEO добавлял и приоритизировал бэклог в Asana, разработчик брал задачу в работу, делал ее за короткий промежуток времени, пушил на тестовый сервер, проверял, после этого в консоли писали git push и код появлялся на продакшене. Да, иногда сервис падал, но это было не так критично, потому что и пользователей, и сотрудников мало.

К 2016 году в отделе работало 4 человека, а количество задач и зависимостей увеличивалось. Появились руководители отделов, 40 сотрудников, новые проекты и разработчики. В результате, процесс разработки перерос в нечто большее сам по себе.

Задачи в Asana игнорировались. Руководители отдела прибегали в кабинет к разработчикам и давали по задаче раз в 3-4 часа. Формализованные задачи постоянно откладывались, из-за неточных спецификаций часто приходилось переделывать то, что уже сделали, а в коде можно было разобраться только сегодня, через 2-3 дня, чтобы понять, что происходит, нужно было времени больше, чем эта задача делалась.

В апреле 2017 года отсутствие процессов привело к тому, что разработчик в первый рабочий день убил базу, а пуши с текстом “HOT FIX” стали появляться ежедневно. В итоге этого хаоса даунтайм сервисов в день достигал 20-30 минут, было больно и пора что-то менять.

Культура кода

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

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

Сначала начали изучать опыт команд, которые росли, и первое, что сделали, это ввели обязательное правило создания Pull Request’ов перед слиянием ветки в мастер. Как показал изученный опыт, переход к использованию практики слияния feature-branch в стабильные ветки через pull-request неизбежен, будь это исправление бага или внедрение нового функционала.

Сейчас процедура слияния веток через pull request следующая:

  • Разработчик вносит новые изменения или правит баги в своей ветке.
  • Проверяет работоспособность и корректность поведения кода локально.
  • Создает pull request, указывая в качестве source branch свою ветку, а в качестве target branch одну из стабильных веток, например, master.
  • Назначает ревьюверов для pull request-а.
  • Ревьюверы проверяют код, пишут замечания, и одобряют слияние веток с замечаниями или просят разработчика их исправить.
  • При отсутствии замечаний или при наличии незначимых замечаний ревьюверы одобряют pull request.
  • При наличии необходимого количества одобрений разработчик вручную сливает свою ветку в ту ветку, которая предназначена для тестирования.
  • После прохождения тестирования pull request может быть замерджен в production ветку. Если в ходе тестирования выявлены ошибки, то разработчик исправляет их и, в зависимости от масштаба и значимости, отправляет снова на ревью или сливает в ветку для тестирования.

Главные плюсы введения Pull Request’ов – снижение время даунтайма сервисов. Если раньше приходилось вносить изменения в случае недоступности сервиса на продакшене, то теперь можно быстро, в один клик, откатить изменения и исправить в спокойном, не авральном режиме.

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

  • Кто вносил рассматриваемые изменения в код;
  • Когда эти изменения сделаны;
  • Для какой задачи эти изменения сделаны.

Оформляется commit messages следующим образом:

  • 1 строка коммита. Summary изменений в коммите, не больше 80-ти символов в длину. Формат для Summary изменений: <Выполненное действие> <над конкретным объектом>. Примеры: Добавлен перевод на итальянский, изменена модель заказа, исправлен баг импорта товаров;
  • Отступ в одну строку;
  • Блок с кратким описанием сделанных изменений длинною в 3-4 строки; В этом блоке описывается контекст сделанных изменений: зачем сделано и почему сделано так;
  • Отступ в одну строку;
  • Сcылка на задачу.

Ссылка на задачу крайне полезная вещь. Иногда нельзя спросить у разработчика, почему сделаны такие изменения. Тогда, открыв задачу по указанной ссылке, становится понятно, почему сделано именно так. А учитывая, что у нас связка Jira + Bitbucket – это оказалось еще удобнее.

Для именования веток используется следующий стандарт: <change-type>/<task-number>-<summary-description>:

  • change-type -feature, для внесения нового функционала, bugfix, для исправления найденных ошибок;
  • task-number – номер задачи, который присваивается Jira автоматически при создании.
  • summary-description – краткое описание на английском, состоящее максимум из 3-4 слов, отображающее изменение.

Jira и Bitbucket позволяют просматривать в задаче в Jira внесенные изменения в репозиторий. Это происходит автоматически, как только ветка с нужным именем попадает в репозиторий. Влияет на этот блок <task-number> в названии ветки. Например, для задачи SUP-104 создается ветка с именем feature/SUP-104-pull-request-bitbucket. После того, как ветка будет создана в репозитории, в задаче появится ссылка на эту ветку.

Правильная задача – половина успеха

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

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

Сначала идея сотрудника компании попадает в бэклог с названием “Идея!”. Далее, каждая задача приоритизируется и переходит в статус “Исследование”.

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

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

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

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

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

Еще нужно отметить, что и сама задача стала оформляться иначе. Раньше это было простое указание к действию, например, “Добавить возможность просмотра заказа”. Теперь же каждая задача идет в контексте проблемы. Сначала формулируется проблема, на основе нее предлагается решение. Это помогает быть в контексте изменений каждому разработчику, а также, часто разработчик может предложить лучшее решение.

Вообще этот процесс – это Design Thinking, которую подстроили под процесс разработки.

Новый процесс разработки

Теперь расскажу, как поменялся воркфлоу непосредственной разработки. Добавили 2 новых статуса “Ревью” и “Задеплоено”.

Ревью делается по следующим причинам:

  • Ревьюверы могут обнаружить ошибочное поведение в коде, которое может привести к проблемам на production-серверах, что в свою очередь может вызвать потерю данных или замедление/остановку работы других отделов компании.
  • Ревьюверы могут предложить более оптимальное решение той или иной задачи, показать/предложить “лучшее” и более качественное решение.
  • Ревьюверы могут писать замечания по оформлению кода. Нужно использовать единый корпоративный стандарт, соблюдать pep8, комментировать код и т.д.
  • В ходе ревью разработчики видят изменения и, как следствие, видят картину изменений в проекте.
  • Ревью позволяет обучаться другим, находя ошибки в своем и чужом коде.
  • Нет плохого кода :)

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

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

Кроме того, есть legacy код, которому 3-5 лет. Как только поддержка кода, а также разработка нового функционала становится дороже, чем рефакторинг, сразу же делаем это. Ну ладно, не сразу же, но делаем :)

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

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Екатерина Рыжкова", "author_type": "self", "tags": [], "comments": 14, "likes": 15, "favorites": 17, "is_advertisement": false, "subsite_label": "dev", "id": 57088, "is_wide": false, "is_ugc": true, "date": "Thu, 31 Jan 2019 15:38:26 +0300" }
{ "id": 57088, "author_id": 3488, "diff_limit": 1000, "urls": {"diff":"\/comments\/57088\/get","add":"\/comments\/57088\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/57088"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 235819, "possessions": [] }

14 комментариев 14 комм.

Популярные

По порядку

Написать комментарий...
0

Олег, сколько времени у вас занимает цикл от рисеча до запуска результатов разработки в продакшен через все статусы, которые вы описали (+/- в среднем, при реализации условной усредненной юзерстори)? Также интересен формат в котором вы анализируете собранные метрики, которые улучшились/ухудшились после переноса на прод. - этим занимается продукт оунер, команда разработки, которая делала (чтобы понимать суть), СЕО?

Ответить
1

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

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

Метрики собираем двумя способами:
1) Трекаем прямо в базу данных, а потом выгружаем.
2) Через MixPanel.

Также для а/б тестирования мы написали свой небольшой пакет: https://github.com/expert-m/react-split-testing

Ответить
0

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

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

Ответить
0

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

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

Ответить
0

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

Ответить
1

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

Ответить
0

Добавила в закладки. Теперь и у меня есть техническая часть истории развития сайта Supl.biz

Ответить
0

У нас один в один, только в Jira доска аналитики больше - + ревью разработчика/тестировщика+чеклисты. Ветки по другому названы - [#таска из jira], develop, release-*, master. + Stage контур для регрессии и проверки хот-фиксов.

Ответить
0

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

Ответить
0

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

Да, и пропагандируем, чтобы больше так никто не делал :)

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

Можете, пожалуйста, поделиться?

Ответить
0

Думаю смогу предложить если детально пообщаться.

Ответить
0

В контакте есть ссылка на мой Линкедин профайл. Пишите.
P.S. пишу с телефона заранее прощу прощения за возможные очепятки

Ответить
0

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

Ответить
0

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

Ответить
0
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ]
Хакеры смогли обойти двухфакторную
авторизацию с помощью уговоров
Подписаться на push-уведомления
{ "page_type": "default" }