company_banner

Кто, где, когда: система компонентов для разделения зон ответственности команды

    Меня зовут Евгений Тупиков, я ведущий PHP-разработчик в Badoo и Bumble. У нас в команде более 200 бэкенд-разработчиков, которые работают над сотнями модулей и отдельных сервисов в наших приложениях. Но поначалу всё было не так масштабно. В 2006 году это был один проект, над которым работала небольшая команда. Каждый разработчик хорошо понимал, как всё устроено: легко ориентировался в коде, знал, какие есть сервисы и как они взаимодействуют между собой. Однако по мере роста проекта всё больше времени занимал поиск «хранителей знаний» — тех, кто отвечает за ту или иную функциональность и к кому можно обратиться с вопросом или предложением. 

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

    Предыстория

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

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

    • @team — команда, ответственная за данную часть системы;

    • @maintainer — человек, разрабатывающий данную функциональность (таких сотрудников может быть несколько).

    /**
     * @team Team name <team@example.com>
     * @maintainer John Smith <john.smith@example.com>
     * @maintainer ....
     */

    Такой подход очень просто внедрить и использовать. Например, можно настроить шаблон в PhpStorm — и нужные теги будут автоматически проставляться при создании нового файла. Мы ещё сделали отдельный Git hook, следящий за тем, чтобы у всех файлов были проставлены нужные теги в требуемом формате.

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

    Нам же хотелось иметь возможность обновлять список ответственных в одном месте. И желательно, чтобы все остальные системы автоматически подхватывали изменения. 

    Так мы пришли к компонентному подходу.

    Что такое компонент

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

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

    При переходе на компонентный подход мы сформировали ряд правил и ограничений:

    • структура компонентов должна быть линейной;

    • каждая команда должна иметь свой набор компонентов (один и тот же компонент не может относиться к нескольким командам);

    • для каждого компонента должен быть задан список ответственных, при этом оптимальное количество ответственных за один компонент — два—четыре человека;

    • только менеджер или тимлид команды может добавлять и удалять компоненты;

    • у каждого компонента должен быть уникальный идентификатор (alias).

    Для управления компонентами у нас есть специальный интерфейс в интранете. Страница компонента выглядит следующим образом:

    Пример страницы компонента в интранете
    Пример страницы компонента в интранете

    Мы видим, что у компонента есть:

    • уникальный идентификатор;

    • email;

    • название команды, которая отвечает за данный компонент;

    • название проекта, к которому относится компонент, в Jira;

    • краткое описание, которое помогает понять, для чего нужен компонент и какая у него зона ответственности;

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

    Как мы используем компоненты в коде

    Докблок файла

    /**
     * @component component_alias
     */

    Мы доработали Git hook, проверяющий докблок. Он следит за тем, чтобы файлы, в которые были внесены изменения, содержали тег @component и чтобы указанный компонент существовал. 

    remote: ERROR in SomeClass.php:        
    remote: * Unknown @component: UnknownComponent. You have to create component before using it in the code   

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

    Сервис для работы с компонентами в коде

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

    $componentManager = new \Components\ComponentManager();
    $component = $componentManager->getComponent('component_alias');
    
    $recipients = [];
    foreach ($component->getMaintainers() as maintainer) {
        $recipients[] = $maintainer->getEmail();
    }

    или найти дежурного по компоненту:

    $componentManager = new \Components\ComponentManager();
    $component = $componentManager->getComponent('component_alias');
    
    foreach ($component->getMaintainers() as $maintainer) {
        if ($maintainer->isDuty()) {
            return $maintainer;
        }
    }

    Интеграция с PhpStorm

    В нашем блоге есть статья про плагин для PhpStorm, который значительно упрощает жизнь разработчикам. За то время, что прошло с момента её написания, мы реализовали много новых удобных фич и добавили поддержку компонентов.

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

    Дежурный по компоненту

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

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

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

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

    Интеграция с внутренними системами

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

    Система сбора и анализа PHP-ошибок

    Исторически для сбора и анализа PHP-ошибок мы используем самописную систему, которая по функциональности похожа на популярные Sentry и Splunk, но адаптирована к нашим внутренним процессам. В неё первую мы добавили поддержку компонентов. 

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

    Эту информацию можно использовать:

    • для поиска ошибок по определённому компоненту;

    • для построения отчётов и графиков в разбивке по компонентам.

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

    Реестр баз данных

    Бэкенд наших приложений Badoo и Bumble состоит из сотен различных модулей, систем и сервисов. Большинство из них для хранения данных использует MySQL.  

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

    1. Найти в коде, на каком хосте живёт база.

    2. Подключиться к хосту через любой удобный инструмент (консольная утилита, phpMyAdmin, Sequel Pro, IDE и т. д.).

    3. Найти нужную базу и таблицу.

    4. Изучить информацию о таблице.

    А если нужно узнать размер таблицы на продакшене? 

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

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

    Чтобы упростить жизнь разработчикам, мы создали систему под названием DBRegistry. Она хранит в себе информацию для всех баз данных, доступную через INFORMATION_SCHEMA.

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

    Информация о компоненте будет полезна в случае, если с каким-то сервером начались проблемы (например, выросла нагрузка на CPU) и администратор баз данных нашёл проблемный запрос и хочет сообщить о проблеме разработчику. Он просто находит нужную таблицу в DBRegistry, смотрит, к какому компоненту она привязана, и пишет о проблеме дежурному.

    Заключение

    С момента нашего перехода на компонентный подход прошло больше трёх лет, и за это время поддержка компонентов была реализована во всех внутренних системах. Нам удалось сделать процессы более быстрыми и понятными. Теперь ни у кого не возникает вопроса «А кто отвечает за этот код?». Достаточно открыть интранет и найти нужный компонент — он содержит всю необходимую информацию.

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

    На этом всё. Спасибо за внимание!

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

    А как у вас организовано разделение зон ответственности?

    • 2,9%комментарии в коде1
    • 11,8%документация в Wiki4
    • 14,7%компонентный подход5
    • 64,7%команда небольшая, все обо всём знают22
    • 5,9%свой вариант — напишу в комментриях2
    Badoo
    Big Dating

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

      +1
      > Компонент — это не обязательно функциональность, непосредственно связанная с кодом. Компонентом может быть и организационный процесс, например выпуск новой версии приложения.

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

        Если из выдуманных, то может быть компонент «Маркетинг» или компонент «Дизайн внутренних интерфейсов». Но понятно, что основной профит от компонентов виден в привязке к коду/фичам/сервисам.
        –3
        200 бекендеров? Это примерно 40 команд. Я может не вкурсе, но разве у вас не сайт на пхп и приложение для знакомств?

        Я слабо разбираюсь в разработке и управлении командами, так как не разработчик. Но кажется, имея под рукой 200 бекендеров можно делать какие-то невероятные вещи. Помню в лучшие времена в вк было 12 фулл стек чуваков.
          +3
          Я слабо разбираюсь в разработке и управлении командами

          Можно было на этом закончить :)
          +3

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

            0
            нужен способ легко найти ответственного за ту или иную часть системы
            Так и не понял, как вы решили эту задачу.
            «Дежурства», как мне кажется, не помогут в случае увольнения автора кода.
              0
              Решили следующим образом: у нас есть лишь источник истины — это система компонентов. Предположим мне потребовалось в рамках моей задачи отправлять push уведомления. Я иду в Интранет, ищу компонент отвечающий за push-ы, смотрю кто дежурный и задаю свой вопрос ему.

              Может получиться так, что дежурный не знает ответа на мой вопрос, и это нормальная ситуация (нет человека который все обо всем знает). Цель дежурства — это в первую очередь решить проблему смены контекста. И в случае, если я как дежурный, не могу сразу ответить на вопрос, то вместо того, чтобы «скинуть» вопрос тому, кто больше погружен в детали конкретного компонента, я начинаю разбираться в проблеме, начинаю задавать уточняющие вопросы коллегам, тем самым пополняя свой багаж знаний.

              Дежурства конечно не помогут в случае, если автор кода уволился и не передал компонент другому человеку. Но это уже признак того, что есть проблемы с обменом знаниями внутри команды и тут никакие компоненты проблему не решат :) Но как минимум система компонентов может показать, что есть компоненты у которых Bus factor = 1 и это маркер того, что в этот компонент нужно инвестировать время и увеличить bus factor как минимум до 2

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

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