История стартапа о внедрении CI / CD Pipelines и процессов разработки

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

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

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

Вот некоторые (вполне нормальные для небольшого стартапа) проблемы, с которыми мы столкнулись:

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

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

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

Непрерывная интеграция и непрерывное развертывание

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

Конвейер CI / CD по сути представляет собой набор шагов, которые выполняются последовательно (или частично параллельно). Типичный конвейер обычно состоит из этапов сборки, тестирования и развертывания. Однако вы можете добавлять другие шаги и расширять конвейер по своему усмотрению.

Возможно, реализация конвейера CI / CD - самый важный шаг, который можно предпринять для масштабирования технологии. Это не только было нашим ощущением, это также может быть подтверждено данными, поскольку, согласно опросу GitLab 2020 DevSecOps, 83% разработчиков заявляют, что выпускают код быстрее и чаще с помощью CI / CD.

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

Уменьшение размера репозитория

Мы начали путешествие с того, что определили, что размер репозитория был чрезмерно большим, в основном из-за большого количества зарегистрированных больших двоичных файлов. Можно подумать, что хранилище дешево, так в чем же на самом деле вред? Ответ прост: чем больше репозиторий, тем больше времени потребуется на выполнение таких команд git, как выборка, извлечение и клонирование. Для нас репозиторий размером ~ 10 ГБ часто занимал больше часа, чтобы просто клонировать. Это, конечно, можно было бы немного уменьшить, используя мелкие клоны, но все же. Основная проблема заключалась не в разработчиках, работающих с базой кода, поскольку клонирование репозитория больше похоже на единичное событие, чем на повседневную задачу. Проблема возникает, когда вы добавляете к нему конвейер CI, поскольку тогда бегуну нужно будет регулярно создавать клон (хотя и неглубокий), возможно, несколько раз в день.

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

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

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

Установка базового уровня CI

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

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

Мы начали с того, что переписали или просто удалили неудачные юнит-тесты. Если над тестом требовалось слишком много работы, чтобы исправить его, мы просто удаляли его. Философия заключалась в том, что нам нужна эта базовая линия, чтобы использовать ее как ступеньку. Мы также добавили несколько простых интеграционных тестов, чтобы убедиться, что конечные точки работают с наиболее простым выходом. Итак, конвейер теперь состоял из двух этапов; юнит-тесты и интеграционные тесты. Эти два шага привели к колоссальному (😉) 10% покрытию кода. Но эй, это было начало на правильном пути.

Процесс развития

Если бы ваш стартап был чем-то вроде нас, история Git (в лучшем случае) выглядела бы примерно так:

Видите проблему? Куча коммитов в лучшем случае с кратким объяснением. Мы определили, что обращение к git blame (либо из терминала, либо внутри IDE) часто не дает никакого представления о том, почему конкретный фрагмент кода был написан именно так и не мог быть легко сопоставлен с любые бизнес-требования.

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

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

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

  • Никаких изменений в тестах.
  • Оставил TODO: s в измененных файлах.
  • Увеличенное количество ошибок, определенное сторонней библиотекой (Spotbugs).

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

Расширение конвейера CI

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

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

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

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

Последние мысли

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

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

- Иосиф Гефро

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

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