“Почему мы выпускаем новые функции так медленно?” — думал я спустя год после присоединения к быстро растущему стартапу. Релиз каждой новой функции был болезненно долгим. Это не то, чего я ожидал — стартапы должны двигаться быстро. Особенно, если вы ещё не нашли свою нишу.
В начале моей работы у нас было три команды разработки. Спустя год и несколько миллионов инвестиций у нас в распоряжении было девять команд разработки. Наша способность к разработке утроилась, но новые функции выходили с той же скоростью. В чем же дело? Я думаю, что дело было в законе Брукса, который утверждает, что добавление новых разработчиков в проект на поздней стадии, приводит к ещё большему замедлению проекта. Мы наняли многих отличных разработчиков, но им нужно было понять что замедляло остальных сотрудников.
- Наш мотивирующий постер для разработчиков
Спустя полгода мы по-прежнему выпускали новые функции медленно. Поэтому, очевидно, закон Брукса был уже не при чем. Должна была существовать ещё одна причина, но какая? Потом мы столкнулись с проблемой в производстве, которая указала мне на возможного виновника.
Проблема и урок
Мы только выпустили новую функцию фильтрации по тегам. Поддержка получала жалобы о том, что функция не работает. Первым делом я попытался загрузить картинку и отметить её как “Не хот-дог”. Потом я попробовал отфильтровать картинки по этому тегу и убедился, что фильтрация не работает. Я составил список команд, которые участвовали в создании функции. Каждая команда была ответственна за свой компонент:
- Загрузка и обработка изображений.
- Elasticsearch.
- Фронтенд фотоальбома.
- Команда DevOps, ответственная за инфраструктуру и развертывание новых функций в облаке.
Я решил сначала поговорить с командой по загрузке изображений. Я спросил руководителя команды, почему их функция не работала. Он посмотрел, что теги были сохранены и предположил, что они не были проиндексированы, что привело меня к команде Elasticsearch. Разработчик из следующей команды заглянул в Elasticsearch. Тег был проиндексирован, все выглядело отлично. Поэтому он отправил меня к команде фронтенда альбома.
Они обнаружили, что фронтенд-библиотека была развернута в более старой версии. Функция фильтрации зависит от этой библиотеки, поэтому это, вероятно, было причиной проблемы. Фронтенд-команда посоветовала мне поговорить с командой DevOps, чтобы они развернули нужную библиотеку.
Я устал ходить от одной команды к другой и сказал им, чтобы они сразу исправили эту проблему. Наконец проблема была решена. Я понял, что командам не хватало ответственности за то, что они создавали. Команды были разбиты по компонентам, и никто из них не чувствовал ответственности за функции — каждый работал только над маленьким кусочком пазла. Разбитие команд по компонентам сделала быстрый выпуск новых функций сложным. Все их усилия были тесно связаны. Если одна из четырех команд не работала или задерживалась, это влияло на выпуск всего функционала.
Структура команды: по компонентам
В начале разработки продукта команды часто самостоятельно разбиваются по компонентам. Если сам продукт пока не имеет структуры, то технические компоненты для его работы понятны. Поэтому команды проще организовать по этим компонентам. Эта идея довольно проста — каждая команда ответственна за один или более компонентов. Такая структура помогает увеличить продуктивность разработчиков, которые работают над конкретными частями системы. Создание чего-то нового на Amazon RedShift? Только одна команда должна беспокоиться об изучении RedShift.
Недостаток таких команд заключается в том, что менеджеры должны беспокоиться о выпуске функций. Границы между командами создают зависимости, которые требуют активного управления. Это возможно, если у вас не так много компонентов или функции не распределены между множеством команд. Представьте, что вы владелец продукта и хотите внедрить Feature 2, как на картинке ниже. Она зависит от двух компонентов, за которые отвечают разные команды. Функция выйдет, только если обе команды завершат необходимую работу над своими компонентами. Вернемся к примеру с фильтрацией тегов. Так как даже причину неисправности было сложно установить, представьте, как неэффективно было заставлять все эти команды координироваться ради одной маленькой функции. Любая задержка в любом компоненте вызывала задержку релиза функции. Чем больше компонентов и команд вовлекалось в дело, тем больше возникало проблем с координацией и вынужденного ожидания. Если один спринт одной команды проваливается, функция не может быть выпущена.
Структура команды: по функциям
Вместо того чтобы делать команды ответственными за компоненты, вы можете сделать команды ответственными за функции. Представьте, что одна команда была бы ответственна за фильтрование и поиск, а другая — за метаданные к изображениям. При выпуске фильтрации по тегам вам нужно было скоординировать усилия всего двух команд. При этом ответственность за компоненты делится, и несколько команд могут работать над изменениями в одних и тех же компонентах. Усилия по координации смещаются с уровня функции на уровень компонентов. Управлять на уровне функции сложно, но это проще, чем управлять на уровне компонентов. Вам нужно убеждаться, что изменениями в компонентах занимаются люди, которые больше других в компании знают об этих компонентах, а также все команды должны разбираться в одних и тех же технических вопросах.
Переход к командам по функциям
Многие команды начинают с команд по компонентам, потому что структура продукта неясна в начале существования проекта. Но в какой-то точке команды по компонентам могут начать вас тормозить. Вот симптомы, которые подскажут вам, что ваша структура вас замедляет:
- Разработка небольших функций занимает больше времени, чем вы ожидали, потому что они находятся в зонах ответственности разных команд.
- При возникновении проблем все показывают пальцем друг на друга. Никто не несет ответственность за функцию.
Если это происходит, структура команды подлежит изменению. Вам стоит рассмотреть переход на команды по функциям. Такая работа имеет свои сложности: вам нужно будет придумать лучшее распределение компонентов между командами и подстроить свою архитектуру, чтобы разъединить как можно больше компонентов. Переход к командам по функциям — это самая сложная часть работы с подобной структурой. Как структурировать команды? Как передавать знания? Как убедиться, что организация разрешит вам изменить структуру? Этому будет посвящена уже другая статья.