J.A.R.V.I.S. — невидимый помощник Leo

    Рано или поздно IT-проекты сталкиваются со сложностями поддержания высокого качества кода и/или увеличивающимся временем доставки изменений в production. Lingualeo испытала на себе все проблемы роста и готова поделиться своей историей повышения эффективности разработки. О том, как это происходило, рассказал teamlead инфраструктурой команды Lingualeo Михаил Кабищев.
    image

    Как и любая другая технологическая компания, Lingualeo проходила через несколько этапов:

    • Начало разработки продукта. Разработка и отладка происходит на одном-единственном сервере, где запущено все, что нужно проекту. Ошибки бывают часто, но это не страшно, т.к. это все лишь прототип, и живых пользователей там еще нет.
    • Появление первых пользователей. Компания начинает ощущать цену ошибок и проблем на продакшене. Уже нельзя править все на продакшене, приходит понимание того, что нужно мыслить релизами. Разработчики внедряют workflow для работы с кодовой базой, появляется что-то вроде stage-сервера, на котором тестируются релизы.
    • Рост проекта и команды. В разработке одновременно находится большое количество задач. Требования к процессу и качеству кода сильно возрастают. За всем очень тяжело следить: кто-то забывает запустить юнит-тесты, кто-то не знает, куда и как нужно задеплоить очередную задачу для тестирования.

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

    Интуитивно команда Lingualeo достаточно быстро понимала, что пора переходить на следующий этап. Иногда переход происходил в правильном русле, а иногда не очень. В какой-то момент в компании сформировались несколько команд разработки, которые работали в одной общей системе. У каждой команды был свой проект в jira, собственная CI-система (система Continuous Integration), умеющая запускать юнит-тесты, разворачивать площадки для тестирования, собирать и деплоить релизы.

    Звучит вроде бы неплохо, да?

    Но все равно в этом подходе были моменты, которые нам не очень нравились:

    Большое количество проектов в jira. Изначально все использовали один workflow. Но потом одна команда добавила дополнительное поле, другая — промежуточный статус и так далее. Из-за этого постоянно приходилось вносить изменения в CI-систему, и иногда эти изменения конфликтовали друг с другом.

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

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

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

    • Вся разработка должна “переехать” в единый проект в jira с единым для всех workflow
    • Можно использовать готовый build-сервер вместо самописного
    • Система деплоя должна быть универсальной, чтобы деплоить разные приложения

    Единый проект в Jira


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

    Task

    Это задачи, которые не требуют написания кода и имеют простейший flow:

    image

    1. Task. Просто задача. Это может быть research, добавление нового репозитория, написание документации и т.д.
    2. Migration. Все миграции для базы данных мы выполняем вручную. Вручную не означает, что мы делаем это прямо в sql-консоли, для этого есть ряд инструментов, но их запуск происходит вручную.


    CI-unit

    image

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


    Такие задачи уже требуют написания кода в одном из репозиториев. В качестве фреймворка для работы с кодом мы используем gitflow, поэтому разделение следующее:
    1. feature
    2. bug
    3. hotfix

    Назначение типов можно прочитать в любом описании про gitflow. Мы разделили feature и bug на отдельные типы, так Lingualeo удобнее расставлять приоритеты для задач и вести аналитику. Раньше каждая команда имела свою собственную agile-доску со своими задачами, это было очень удобно, и нужно было придумать способ разделения задач по командам.

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

    • Team. Имя команды, владеющей задачей.
    • Component. В нашем случае component однозначно указывает на репозиторий. Хотя jira и позволяет указывать несколько значений в поле component, проблем с этим у нас не возникало.
    • FixVersion. Номер релиза, в который попадет(попала) задача.
    • Reporter. Человек, который создал задачу.
    • Customer. Это заказчик задачи. По умолчанию он равен reporter`у, но может быть изменен. Именно он производит приемку задачи.
    • Developer. Разработчик, ответственный за задачу. У задачи могут подзадачи, которые будут делать разные люди, но именно developer отвечает за задачу целиком.
    • QA Engineer. По аналогии c developer`ом, этот человек отвечает за тестировании задачи целиком.
    • Code reviewer. Все задачи (ну ладно, почти все) у нас проходят code-review, и это тот человек, который этим занимается.


    Build-сервер


    Точно так же, как и все, команда Lingualeo смотрела на трех основных игроков на этом рынке: Jenkins, Teamcity, Bamboo. Потестировали все три, но больше всего понравился Teamcity: бесплатный, есть удобный REST API и приятный интерфейс.

    Так кто такой J.A.R.V.I.S.?


    Итак, мы получили следующую конфигурацию: jira — для ведения задач, teamcity -для прогона тестов, сборки релизов и т.д и github — для хранения кода. Нам нужно было подружить все эти системы вместе.

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

    image

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

    Мы хотели, чтобы J.A.R.V.I.S. умел делать за нас следующие операции:

    • проверять код на соответствие стандартам
    • запускать unit-тесты
    • создавать и мерджить pull request`ы в github`e
    • создавать тестовые стенды для тестирования задач
    • создавать релизные и хотфиксные ветки
    • производить деплой

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

    start inspection:
        search:
            jql: 'project="DEV" AND status="Ready for Inspection"'
        action:
            type: 'run-build'
            params:
                buildTypeId: 'CodeInspection_%component%'
            success:
                transition: 'start inspection'
            fail:
                transition: 'fail inspection'
    complete inspection:
        search:
            jql: 'project="DEV" and status="On inspection"'
        action:
            type: 'check-build'
            params:
                buildTypeId: 'CodeInspection_%component%'
            success:
                transition: 'complete inspection'
            fail:
                transition: 'fail inspection'
    
    

    Первое правило находит все тикеты со статусом Ready for Inspection и запускает соответствующую конфигурация в teamcity, которая проверяет код на соответствие стандартам, а также прогоняет все unit-тесты. Если запуск конфигурации удался, то к тикету применяется переход start inspection, и он переходит в статус On Inspection. Второе правило проверяет все запущенные конфигурации. Если она успешно завершилась, то тикет переходит дальше. Если во время сборки произошли ошибки, то тикет через переход Fail Inspection возвращается назад к разработчику.

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

    Сам J.A.R.V.I.S. разрабатывается и собирается тоже с помощью J.A.R.V.I.S.`a :)

    Благодаря унификации процесса разработки в компании и создании J.A.R.V.I.S.`a мы смогли повысить качество выпускаемого нами кода и уменьшить время доставки изменений в production, уменьшили время разработчиков, которое тратилось на рутинные операции.
    Lingualeo
    Компания

    Комментарии 20

      –1
      Может быть тут будет наконец-то услышано и замечено — в мобильной версии автоматом прибавляются фрикадельки, за каждое обновление, у меня при средней активности использования порядка 5к. А ведь это мотивация для монетизации, с большим количеством единиц данного ресурса почти так же хорошо, как с премиумом. В саппорт писал — "Мы знаем, мы исправим". Месяца полтора прошло :)
        0
        Будет продолжение с подробностями про те задачи, которые выполняет Тимсити и как они соотносятся с флоу задач/релизов в джире?
          0
          Про процесс релизов обязательно напишем отдельную подробную статью. Про teamcity интересует настройки конкретных конфигураций для запуска тестов, сборки тестовых площадок и т.д.?
            0
            Нет, конкретные конфигурации не нужны. К сожалению, картинка в разделе CI-unit не раскрывает, что же кроется за терминами Inspection, Integration и как они связаны с флоу в джире. И да, почему шаг Review не автоматический? Пулл-реквесты в гитхабе создаются же автоматически?
              0
              Подробно по этапам:

              Inspection. Тут мы проверяем качество кода. Для php-проектов мы запускаем проверку на соответствие стандартам psr и юнит-тесты, а также измеряем покрытие тестами. Если проверка или тесты упали, то задача возвращается назад разработчику.

              Review. Автоматически создаем pull request и отправляем тикет review`eру.

              Build. Этап для создание тестовой сборки. Для php мы создаем тестовую площадку (про них ответил чуть ниже), для мобильных приложений собираем полноценный билд.

              Testing. Непосредственно тестирование задачи.

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

              Integration. Мы используем gitflow и на этом этапе наступает именно тот момент, когда ветку с задачей нужно интегрировать в develop/master. На самом деле тут просто происходит merge pull request`a :)

              Release. Релиз

              Для каждого этапа в нашем флоу в jira существует 3 статуса:

              • Ready for step
              • On step
              • step failed

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

              Статусов суммарно получается действительно много, но благодаря им мы в любой момент времени можем абсолютно точно сказать, что происходит с задачей. Все наши команды используют доски в джире и поэтому разработчикам нужно просто перетаскивать карточки из одной колонки в другую, а не думать над большим кол-вом статусов. Все, что можно сделать автоматически, за них сделает J.A.R.V.I.S.
          +5
          Миша, а где же ссылка на github ???
            0
            Жень, система сейчас закрытая. Возможно, пока :) Проблема не в том, чтобы принять решение и открыть исходники, это достаточно просто. Помимо этого нужно проделать очень и очень большую работу по написанию хорошей и подробной документации с примерами использования, а это к сожалению сейчас не в приоритете.
              0
              если сделаешь приватный repo, я готов взяться за написание документации и адаптировать под опенсорц
                0
                Хорошо, мы подумаем над этим :)
                  0
                  ну как подумали? ;) чего надумали?
            0
            А какую аналитику для Jira используете если не секрет?
              0
              Используем разные отчеты по scrum/kanban доскам, а также некоторые виджеты. Никакой специальной внешней аналитики для jira у нас нет, да и вроде бы она нам и не нужна :)
              0
              Рассматривали Buildbot?
                0
                Честно говоря нет. Судя по описанию это аналог только build-сервера (в нашем случае teamcity), а нам нужен был инструмент для управления всем процессом.
                0
                А можете подробнее рассказать о тестовых стендах?
                  0
                  Мы используем достаточно простое, но очень удобное решение: на тестовом сервере nginx настроен таким образом, чтобы пути вида XXX.sandbox.local смотрели на папку /var/www/XXX. Соответственно когда задача проходит ревью и готова к тестированию, J.A.R.V.I.S. подхватывает её и запускает конфигурацию в teamcity, которая состоит из двух основных шагов:

                  • Собрать из ветки feature/dev-123 билд, который включает в себя генерацию js, сss, шаблонов и т.д. и запаковать в tar.gz архив
                  • Загрузить и распаковать архив на тестовом сервере в папку /var/www/dev-123

                  Также каждую ночь у нас автоматически собирается площадка develop.sandbox.local, можно всегда зайти и посмотреть на самую последнюю версию продукта.

                  После того как задача релизится, то J.A.R.V.I.S. автоматически удаляет ненужную площадку, но, если необходимо, мы всегда собрать и запустить площадку из любой ревизизии.
                    0
                    А какая используется бд? Общая или каждый раз клонирует слепок и накатывает миграции?
                      0
                      Сейчас используем одну общую базу на все площадки. Все изменения в схемы БД у нас обратно-совместимы, поэтому проблем со схемой не возникает. Но вскоре все равно планируем уходить от этой схемы.
                  0
                  Первая мысль про JARVIS — Ingress
                    0
                    Где скачать-то?

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

                    Самое читаемое