Сегодня компании, достигшие успеха, все реже прибегают к вертикальному масштабированию для расширения возможностей своих информационных систем. Они предпочитают горизонтальное масштабирование, покупая большое количество компьютеров прежней производительности. Добавляя очередной компьютер к ЦОД, мы увеличиваем его мощность, а чтобы увеличить «мощность» команды, приглашаем в нее нового инженера. И если он впоследствии покидает компанию, это не должно вызвать кризиса. Здесь уместно вспомнить закон Фредерика Брукса, согласно которому увеличение численности исполнителей на проекте, отстающем от графика, вызывает еще большее отставание. Так ли это при горизонтальном масштабировании? Вернемся к вопросу позже, а пока обсудим успешные примеры самоорганизации команд. Возможно, истинное горизонтальное масштабирование и недостижимо, но в большинстве случаев можно приблизиться к идеалу путем оптимизации принципов написания кода и планирования.

Вертикальное и горизонтальное масштабирование команды

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

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

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

Как именно этого добиться? Основной фактор, определяющий возможность подключения к работе нового участника, — это состояние кода. Если вам досталась безупречная кодовая база, авторы которой сделали все, чтобы изложить в ней свои знания, вы легко ее поймете и начнете дорабатывать. Если код даже качественный, но беспорядочный или лишен важных комментариев, то дорабатывать его будет непросто.

Улучшение принципов написания кода

Два десятка лет тому назад я входил в состав объединения Catalysis [1], которое пропагандировало среди разработчиков преимущества объектно-ориентированного программирования, архитектурного планирования ПО и составления точных спецификаций. Разработчиков тогда непросто было убеждать менять привычные методы. Но перенесемся в наши дни: сегодня широко применяются методы функционального программирования, требующие еще более строгой дисциплины и точности, чем мы пропагандировали в свое время.

Сегодня принципы написания кода полностью изменились, включая языки, инструменты и общепринятые нормы. Идеи, казавшиеся раньше труднореализуемыми, теперь легко воплотить в жизнь. Разработчики воспринимают как должное наглядный граф исходного кода, контракты, которые можно просматривать по щелчку, языки с развитыми системами типов, облегчающими написание самодокументированного кода, а также сопроводительные файлы ReadMe, в которых текст и изображения поясняют то, что нельзя выразить в коде, — например, мотивацию принятия того или иного архитектурного решения. Двадцать лет назад всего этого еще не было. Сегодня, если кодовая база поддерживается в идеальном состоянии, горизонтально масштабировать команду гораздо проще — архитектуру системы можно понять, просто читая код. Что именно требуется для обеспечения идеальной чистоты кода? Четкие, согласованные концепции, связывающие предметную область, архитектуру и код; иерархическая «история», описывающая все уровни системы; стиль кодирования, позволяющий легко выяснить архитектуру; повсеместное применение шаблонов проектирования. Допустим, все это есть и код идеально чист. Как поддерживать его в таком состоянии?

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

Все перечисленное служит минимизации технического долга. «Со временем мы накопили знания о приложении, модифицируя программу, чтобы она выглядела так, словно мы на протяжении всего проекта понимали, что делаем, и так, будто добиться этого было легко» — так можно описать процеcc рефакторинга, поясняя метафору долга [2]. Понятие «технический долг» можно использовать в качестве синонима безобразного кода, но мне больше по душе версия, которая звучит так: это «то, что происходит, когда вы позволяете коду со временем портиться, лишая его способности выражать концепции предметной области и ценные архитектурные решения» [2].

Улучшение принципов планирования

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

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

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

Горизонтальную масштабируемость группы можно обеспечить, если у каждого будет некоторое представление о картине в целом, чтобы все участники в индивидуальном порядке могли принимать оптимальные решения. Но что такое «картина в целом»? Существует рекомендация включать в планы работы команды четыре составляющие: функции, дефекты, архитектурную инфраструктуру и технический долг [3]. Все участники группы должны быть в курсе этих планов, при этом вопросы компромиссов при распределении работы над элементами стоит решать сообща. Таким образом, даже при возникновении помех у всех разработчиков будет представление об общих целях, ближайших задачах и компромиссах. Планирование, осуществляемое подобным образом, может быть весьма эффективным. И даже если с этим справляются не все участники команды, со временем ситуация улучшается. А самое лучшее то, что даже в отсутствие архитектора все может идти гладко. Архитектор вносит свою лепту, направляя проект в целом, но в отдельно взятые периоды времени команда справляется самостоятельно.

***

Переход от вертикального к горизонтальному масштабированию когда-то казался непозволительно дорогим, но со временем были разработаны специальные методы, и затраты стали меньше — так же как сегодня благодаря развитию инструментов сократились затраты на рефакторинг и менее затратным стало контрактное программирование за счет совершенствования функциональных языков. И хотя я не готов поспорить с законом об увеличении численности участников проекта, отстающего от графика, но сегодня, когда разработчики имеют дело с чистой кодовой базой, параллельно кодированию в группах может выполняться большой объем работы, в том числе благодаря развитию средств, помогающих улучшить координацию. Когда все идет гладко, это значит, что архитектура в целом согласована, шаблоны проектирования содержат решения для большинства возможных проблем, а сам код (и файлы ReadMe) дает разработчикам ответы на возникающие у них вопросы.

Брукс в книге «Мифический человеко-месяц» выражал беспокойство по поводу цены координации: «Так как создание программного продукта является по сути системным проектом, практикой сложных взаимосвязей, то затраты на обмен данными велики и быстро начинают преобладать над сокращением сроков, достигаемым в результате разбиения задачи на более мелкие. В этом случае привлечение дополнительных работников не сокращает, а удлиняет график работ» [4]. Однако Брукс не давал рекомендаций оставить одного человека на проекте, чтобы устранить затраты на координацию. То есть, если благодаря инструментам и специальным методам удается поддерживать координационные расходы на низком уровне, значит, возможность горизонтального масштабирования достигнута. Все технические методы и процессы, упомянутые в статье, уже применяются, хотя и недостаточно широко. Не все команды справляются с горизонтальным масштабированием, но те, кому это удается лучше других, находятся в авангарде индустрии разработки ПО.

Литература

1. D. D'Souza, A. C. Wills. Objects, Components, and Frameworks wit UML: The Catalysis Approach. Boston: Addison-Wesley, 1998.

2. W. Cunningham. Ward explains debt metaphor. Feb. 14, 2009. URL: http://wiki.c2.com/?WardExplainsDebtMetaphor (дата обращения: 03.09.2019).

3. P. Kruchten, R. Nord, I. Ozkaya. Managing Technical Debt: Reducing Friction in Software Development, 1st ed. Boston: Addison-Wesley Professional, 2019. — P. 62–63.

4. F. Brooks. The Mythical Man-Month. Boston: Addison-Wesley, 1995.

Джордж Фэрбенкс (gf@georgefairbanks.com) — инженер программного обеспечения, компания Google.

George Fairbanks, Scale Your Team Horizontally. IEEE Software, July/August 2019, IEEE Computer Society. All rights reserved. Reprinted with permission.