Перевод статьи подготовлен специально для студентов курса «DevOps практики и инструменты».
В этой статье обсуждается взаимосвязь между структурой кода и структурой организации при разработке программного обеспечения. Я рассуждаю над тем, почему программное обеспечение и команды не могут легко масштабироваться, какие уроки мы можем подсмотреть в природе и Интернете, и показываю как мы можем уменьшить связанность программного обеспечения и команд для преодоления проблем масштабирования.
Статья основана на моем 20-летнем опыте создания больших программных систем и на впечатлении от книги “Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations” (Nicole Forsgren, Jez Humble and Gene Kim), в которой приводятся данные исследований для подкрепления большинства моих утверждений, приведенных здесь. Эта книга настоятельно рекомендуется к прочтению.
Программное обеспечение и команды не масштабируются
Часто первый релиз, возможно, написанный одним или двумя людьми, происходит удивительно просто. В нем может быть ограниченная функциональность, но он написан быстро и соответствует требованиям заказчика. Взаимодействие с заказчиком на этом этапе великолепное, потому что заказчик обычно находится в непосредственном контакте с разработчиками. Любые баги устраняются быстро, а новые фичи могут быть добавлены достаточно безболезненно. Через некоторое время темп замедляется. Версия 2.0 занимает немного больше времени, чем ожидалось. Исправлять баги сложнее, а новые фичи даются, уже не так-то просто. Естественным ответом на это является добавление в команду новых разработчиков. Хотя, кажется, что каждый дополнительный сотрудник, добавленный в команду, снижает производительность. Возникает ощущение, что по мере того, как растет сложность программного обеспечения, оно атрофируется. В крайних случаях организации могут столкнуться с тем, что используются программы с очень дорогой поддержкой, в которые практически невозможно внести изменения. Проблема в том, что вам не нужно делать какие-то “ошибки”, чтобы это произошло. Это настолько распространено, что можно сказать, что это «естественное» свойство программного обеспечения.
Почему так происходит? Есть две причины: связанные с кодом и с командой. И код, и команды плохо масштабируются.
По мере роста кодовой базы одному человеку становится все труднее ее понять. Существуют фиксированные когнитивные границы человека. И, хотя, один человек может держать в уме детали небольшой системы, но только до тех пор, пока она не станет больше, чем его когнитивный диапазон. Как только команда вырастает до пяти или более человек, то для одного человека становится практически невозможно быть в курсе того, как работают все части системы. А когда никто не понимает всей системы, то появляется страх. В большой сильносвязанной системе очень трудно понять влияние любых существенных изменений, поскольку результат не локализован. Для минимизации влияния изменений разработчики начинают использовать обходные пути и дублирование кода вместо выявления общих черт, создания абстракций и обобщений. Это еще больше усложняет систему, усиливая эти негативные тенденции. Разработчики перестают чувствовать ответственность за непонятный им код и неохотно делают рефакторинг. Технический долг растет. Это также делает работу неприятной и неудовлетворительной и стимулирует «отток талантов», когда уходят лучшие разработчики, которые легко найдут работу в другом месте.
Команды также не масштабируются. По мере роста команды коммуникации становятся все сложнее. В игру вступает простая формула:
c = n(n-1)/2
(где n — количество людей, а c — количество возможных связей между участниками команды)
количество членов команды | количество возможных связей |
1 | 0 |
2 | 1 |
5 | 10 |
10 | 45 |
100 | 4950 |
По мере ее увеличения команды ее потребности в коммуникации и координации растут в геометрической прогрессии. При превышении определенного размера одной команде очень трудно оставаться целостной структурой, и естественная человеческая социальная тенденция к разделению на более мелкие группы приведет к формированию неформальных подгрупп, даже если руководство не будет принимать в этом участия. Общение с коллегами становится труднее и будет естественным образом заменено новыми лидерами и коммуникациями сверху вниз. Члены команды превращаются из равноправных стейкхолдеров системы в обычных производственных рабочих. Мотивация страдает, отсутствует чувство сопричастности, обусловленное эффектом диффузии ответственности.
Руководство часто вмешивается на этом этапе и формально подходит к созданию новых команд и управленческих структур. Но, не важно формально или неформально, большим организациям трудно поддерживать мотивацию и заинтересованность.
Обычно в этих патологиях масштабирования винят неопытных разработчиков и плохое управление. Но это несправедливо. Проблемы масштабирования — это “естественное” свойство растущего и развивающегося программного обеспечения. Это то, что происходит всегда, если вы не обнаружите проблему на раннем этапе, не поймете точку отклонения и не приложите усилия для решения проблемы. Команды разработчиков программного обеспечения создаются постоянно, количество программного обеспечения в мире постоянно растет, и большая часть программного обеспечения относительно небольшого размера. Поэтому, довольно часто, успешный и развивающийся продукт создается командой, не имеющей опыта крупномасштабных разработок. И нереально ожидать, что разработчики распознают точку перегиба и поймут, что делать, когда начнут проявляться проблемы масштаба.
Уроки масштабирования природы
Недавно я прочитал превосходную книгу Geoffrey West “Scale”. В ней говорится о математике масштаба в биологических и социально-экономических системах. Его тезис заключается в том, что все большие сложные системы подчиняются фундаментальным законам масштаба. Это увлекательное чтение и я его очень рекомендую. В рамках этой статьи я хочу сосредоточиться на его точке зрения, что многие биологические и социальные системы удивительно хорошо масштабируются. Посмотрите на тело млекопитающего. У всех млекопитающих одинаковые типы клеток, структура костей, нервная и кровеносная система. Однако разница в размерах мыши и синего кита составляет около 10^7. Как природа использует для организмов столь разных размеров одинаковые материалы и строение? Похоже, ответ заключается в том, что эволюция открыла фрактальные разветвленные структуры. Посмотрите на дерево. Каждая его часть выглядит как маленькое дерево. То же самое справедливо для кровеносной и нервной систем млекопитающих, они представляют собой разветвленные фрактальные сети, где небольшая часть ваших легких или кровеносных сосудов выглядит как уменьшенная версия целого.
Можем ли мы взять эти идеи у природы и применить их к программному обеспечению? Я думаю, что мы можем извлечь важные уроки. Если мы сможем построить большие системы, состоящие из небольших частей, которые сами по себе выглядят как законченные системы, то будет возможно сдержать патологии, влияющие на большинство программ по мере их роста и развития.
Существуют ли программные системы, которые успешно масштабируются на несколько порядков? Ответ очевиден — Интернет, глобальная программная система, насчитывающая миллионы узлов. Подсети действительно выглядят и работают как уменьшенные версии всего Интернета.
Признаки слабосвязанного программного обеспечения
Возможность в большой системе выделить отдельные, слабосвязанные компоненты является основным методом успешного масштабирования. Интернет, по сути, представляет собой пример слабосвязанной архитектуры. Это означает, что каждый узел, сервис или приложение в сети имеет следующие свойства:
- Используется общий протокол связи.
- Данные передаются, используя четкий контракт с другими узлами.
- Для коммуникации не требуется знание о конкретных технологиях реализации.
- Версионирование и развертывание осуществляются независимо.
Интернет масштабируется, потому что это сеть узлов, которые взаимодействуют через набор четко определенных протоколов. Узлы взаимодействуют только по протоколам, детали реализации которых не должны быть известны взаимодействующим узлам. Глобальный Интернет не развертывается как единая система. В нем у каждого узла своя версия и процедура развертывания. Отдельные узлы появляются и исчезают независимо друг от друга. Подчинение интернет-протоколам — это единственное, что действительно имеет значение для всей системы в целом. Кто создавал каждый узел, когда он был создан или удален, какая у него версия, какие конкретные технологии и платформы он использует, — все это не имеет отношения к Интернету в целом. Это то, что мы подразумеваем под слабосвязанным программным обеспечением.
Признаки слабосвязанной организации
Мы можем масштабировать команды, следуя тем же принципам:
- Каждая подкоманда должна выглядеть как небольшая организация по разработке программного обеспечения.
- Внутренние процессы и коммуникация команды не должны выходить за пределы команды.
- Используемые технологии и процессы для реализации программного обеспечения не должны обсуждаться вне команды.
- Команды должны общаться между собой только по внешним вопросам: общие протоколы, функциональность, уровни обслуживания и ресурсы.
Небольшие команды разработчиков более эффективны, чем большие, поэтому надо разбить большие команды на более мелкие группы. Уроки природы и Интернета заключаются в том, что подгруппы должны выглядеть как единые небольшие организации по разработке программного обеспечения. Насколько маленькие? В идеале, от одного до пяти человек.
Важно то, чтобы каждая команда выглядела как небольшая независимая организация по разработке программного обеспечения. Другие способы организации команд менее эффективны. Часто возникает соблазн разделить большую команду по функциям. Поэтому у нас появляются команда архитекторов, команда разработчиков, команда DBA, команда тестировщиков, команда развертывания и команда поддержки, но это не решает ни одной из проблем масштабирования, о которых мы говорили выше. В разработке какой-то фичи должны участвовать все команды и часто итеративно, если вы хотите избежать управления проектом в стиле водопада.
Коммуникационные барьеры между этими функциональными командами становятся основным препятствием для эффективной и своевременной поставки. Команды сильно связаны, потому что для совместной работы им нужно делиться важными внутренними деталями. Кроме того, интересы разных команд не совпадают: разработчики обычно получают награду за новые фичи, тестировщики за качество, поддержка за стабильность. Эти различные интересы могут привести к конфликту и плохому результату. Зачем разработчикам беспокоиться о логах, если они их никогда не читают? Зачем тестировщикам заботиться о поставке, если они отвечают за качество?
Вместо этого, мы должны организовывать команды по слабосвязанным сервисам, которые поддерживают бизнес-функции, или по логической группе функций. Каждая подкоманда должна проектировать, кодировать, тестировать, развертывать и сопровождать свое программное обеспечение. Вероятнее всего, члены такой команды будут специалистами широкого профиля, а не узкими специалистами, потому что в небольшой команде придется разделять эти роли. Они должны сосредоточиться на максимально возможной автоматизации процессов: автоматизированное тестирование, развертывание, мониторинг. Команды должны сами выбирать инструменты и проектировать архитектуру для своих систем. Хотя протоколы, используемые для взаимодействия сервисов, должны определяться на уровне организации, выбор инструментов, используемых для их реализации, должен быть делегирован командам. И это очень хорошо согласуется с моделью DevOps.
Уровень независимости, которым обладает команда, является отражением уровня связанности всей организации. В идеале организация должна заботиться о функциональности программного обеспечения и, в итоге, о ценности для бизнеса, которые предоставляет команда, а также о затратах на ресурсы команды.
В этом случае важную роль играет архитектор программного обеспечения. Он не должен фокусироваться на конкретных инструментах и технологиях, которые используют команды, или вмешиваться в детали внутренней архитектуры сервисов. Вместо этого, он должен концентрироваться на протоколах и взаимодействиях между различными сервисами и работоспособности системы в целом.
Перевернутый закон Конвея: структура организации должна моделировать целевую архитектуру
Как согласуются между собой слабая связанность программного обеспечения и слабая связанность команд? Закон Конвея гласит:
«Организации, проектирующие системы, ограничены дизайном, который копирует структуру коммуникации в этой организации».
Это основано на наблюдении, что архитектура программной системы будет отражать структуру организации, которая ее создает. Мы можем «хакнуть» закон Конвея, перевернув его. Организовать наши команды так, чтобы они отражали нашу желаемую архитектуру. Помня об этом, мы должны согласовать слабосвязанные команды со слабосвязанными программными компонентами. Но должны ли это быть отношения один-к-одному? Я думаю, что в идеале, да. Хотя, кажется, что хорошо, если небольшая команда работает над несколькими слабосвязанными сервисами. Я бы сказал, что точка перегиба масштабирования для команд больше, чем для программного обеспечения, поэтому такой стиль организации кажется допустимым. Важно, чтобы компоненты программного обеспечения оставались разделенными, с собственным версионированием и развертыванием, даже если некоторые из них разрабатываются одной командой. Мы хотели бы иметь возможность разделить команду, если она станет слишком большой, с передачей разрабатываемых сервисов разным командам. Но мы не сможем этого сделать, если сервисы сильно связаны или используют совместно процесс, версионирование или развертывание.
Мы должны избегать работы нескольких команд над одними и теми же компонентами. Это анти-паттерн. И, в некотором смысле, даже хуже, чем работа одной большой команды с одной большой кодовой базой, потому что коммуникационные барьеры между командами приводят к еще более сильному ощущению отсутствия сопричастности и контроля.
Взаимодействие между слабосвязанными командами, создающими слабосвязанное программное обеспечение, сведено к минимуму. Возьмем снова пример с Интернетом. Часто можно использовать API, предоставленное другой компанией, без какого-либо прямого общения с ней (если процесс прост и есть документация). При взаимодействии команд не должны обсуждаться внутрикомандные процессы разработки и реализации. Вместо этого должна обсуждаться функциональность, уровни обслуживания и ресурсы.
Управление слабосвязанными командами, создающими слабосвязанное программное обеспечение, должно быть проще, чем альтернативы. Большая организация должна сосредоточиться на предоставлении командам четких целей и требований с точки зрения функциональных возможностей и уровней обслуживания. Требования к ресурсам должны исходить от команды, хотя и могут использоваться организацией для измерения отдачи от инвестиций.
Слабосвязанные команды разрабатывают слабосвязанное ПО
Слабая связанность в программном обеспечении и между командами является ключом к построению высокоэффективной организации. И мой опыт подтверждает эту точку зрения. Я работал в организациях, где команды были разделены по функциям, по уровням ПО или, даже там, где было разделение по клиентам. Также я работал и в больших хаотичных командах на единой кодовой базе. Но во всех этих случаях были проблемы с масштабированием, о которых говорилось выше. Самым приятным опытом всегда было, когда моя команда была полноценным подразделением, самостоятельно занимающимся созданием, тестированием и развертыванием независимых сервисов. Но вам не нужно полагаться на мои истории из жизни. В книге Accelerate (описанной выше) есть данные исследований, подтверждающих эту точку зрения.
Если Вы дочитали этот материал до конца, советуем к просмотру запись открытого вебинара по теме «Один день из жизни DevOps».
Автор: MaxRokatansky