Перевод статьи Duncan Mackenzie «Неизбежный результат фокусировки только на доставке функций»

30.07.2023

Оригинал был опубликован 15 января 2021 в блоге Duncan Mackenzie.

Статья формально про разработку IT-систем. Тем не менее, заодно это и прикладной текст про «системы управления знаниями» (даже автор мельком о подобном упоминает). Принципы, описанные в материале, могут помочь:

  • при выборе конкретного решения для ведения командной «базы знаний» ➜ можно оценить, насколько команда сервиса придерживается схожих подходов в разработке;
  • и при выстраивании в компании процессов по «управлению знаниями» ➜ правильно расставлять приоритеты в работе над системой, принимать более обдуманные решения при добавлении новых функций (или отказе от них).

Профиль экспертизы автора можно обозначить как: engineering management в области систем контента для разработчиков.

Он более 20 лет проработал в Microsoft, в разное время был руководителем команд, которые создавали Docs.Microsoft.com, Azure.Microsoft.com, Channel 9 и т.д. До этого в качестве разработчика, контент-стратега и автора работал с ресурсом MSDN. С конца 2022 года в качестве Engineering Manager работает в Stripe над их «Documentation platform».

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

Примеры, которые автор приводит в своей статье, получаются очень живыми.

Традиционная ремарка. Если позволяет знание английского, то лучше читать оригинал статьи «The inevitable result of focusing only on shipping features». Перевод машинный, хоть и делаю вычитку для уточнения формулировок и терминов, тем не менее, где-то может быть неточно передан смысл тезисов.

Обновлено 30.10.2024: внёс небольшие правки в аннотацию к статье.


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

Представьте себе, что у вас есть строительная компания, которая проектирует и строит небоскребы. Они могут создавать один новый небоскреб в год. Если предположить, что ничего больше не изменится, они должны быть в состоянии поддерживать этот темп в долгосрочной перспективе. Теперь, после завершения каждого нового строительного проекта, пусть та же команда возьмет на себя обслуживание и эксплуатацию этого здания. Это небольшой объем работы по сравнению со всем строительством, но это отнимает часть их возможностей, поэтому на завершение следующего здания уходит 13 месяцев. Теперь они берут на себя и обслуживание этого здания. Они умны, поэтому получают некоторую эффективность, но все равно на завершение следующего здания уходит 14 месяцев. В конце концов, остается так мало членов команды, чтобы сосредоточиться на следующем проекте, что весь прогресс останавливается.

Если мы постоянно добавляем новые функции, общая сложность программного обеспечения будет неуклонно расти, а площадь поверхности (surface area), которую нам нужно поддерживать, вырастет до неустойчивого состояния. Я не говорю, что добавление новых функций плохо, но нам нужно добавлять их, держа в голове долгосрочный продукт (long-term product). Нам нужно видеть постоянный фокус на реструктуризации/улучшении продукта как часть нашей работы, а не как налог, который отвлекает нас от добавления новых функций.

Недавно John Cutler опубликовал в Twitter это изображение, которое затрагивает ту же тему.

The inevitable result of focusing only on shipping features

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

Как это происходит в реальном мире? Я собираюсь использовать Microsoft Docs в качестве примера, когда буду излагать некоторые мысли о том, как команда может избежать полного тупика, когда она не может двигаться вперед.


Постоянное совершенствование сути вашего продукта

Если вы не находитесь на ранней стадии разработки, ваш сайт/приложение/сервис уже представляет ценность для ваших клиентов. В случае с Docs авторы могут создавать и обновлять контент по мере того, как Microsoft поставляет и обновляет продукты. Обеспечение надежности процесса публикации и доступности контента для наших клиентов является нашей основной целью.

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

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

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

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


Постоянное добавление новых функций

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

В Docs команда по контенту создает страницы, используя компоненты и шаблоны, созданные командой разработчиков и дизайнеров. Когда нам нужно создать новый опыт (experience), например, переработанную домашнюю страницу Learn, мы можем обойти нашу систему шаблонов и сделать это в HTML. В краткосрочной перспективе это может сэкономить нам много времени, но мы бы:

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

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

Некоторые будут кричать «преждевременная оптимизация» (premature optimization) и «YAGNI», потому что мы делаем дорогостоящий набор работ для чего-то, что может понадобиться только один раз. В случае с новой страницей в Docs я бы спросил, как долго она будет жить, и если ответ будет больше нескольких месяцев, то я уже знаю, что нам нужно построить устойчивое решение. Обновление и поддержка этой страницы станут обузой для команды, если мы не построим ее правильно с самого начала. Для других запросов функций (feature requests) это не всегда так ясно.

Однажды нас попросили добавить на сайт баннер, рекламирующий предстоящее мероприятие. Это было необычно, и не было возможности узнать, будет ли это постоянной потребностью, поэтому команда решила проблему с помощью нестабильного, одноразового кода. Все еще хороший код, все еще надежный, но его было нелегко обновлять и требовалось инженерная работа для каждого нового баннера. Через некоторое время стало очевидно, что будет постоянный поток этих баннеров, поэтому вот тут-то и возникла необходимость «управлять сложностью» (manage complexity). Мы могли бы просто продолжать делать эти баннеры по одному, но правильным подходом было сделать шаг назад, задокументировать желаемое состояние (где обновление баннеров было самообслуживанием), а затем построить его.

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

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


Расширение команды для решения возросших задач

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

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

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

Например, у Channel 9 есть система видеокодирования, которая необходима для публикации контента, но она также очень изолирована и взаимодействует с сайтом только через некоторые внутренние API. Если бы у нас были ресурсы, наличие отдельной команды в этой системе было бы эффективным способом наращивания наших возможностей.

Однако границы между этими областями должны быть текучими, если новая функция требует изменения системы дизайна, это должно быть чем-то, что может сделать либо команда функций, либо основная команда, а другая команда поможет в review. При жестком разделении обязанностей вы быстро столкнетесь с узкими местами и задержками. В нашем примере с Channel 9, если нам нужно было обновить размер thumbnails как часть функции front-end, может быть проще всего позволить команде «video encoding» сделать это, поскольку они лучше всех знают код, но мы должны быть открыты для того, чтобы команда, которой нужно обновление, просто сделала pull request, который должен быть одобрен командой «video encoding».

Есть много способов создать небольшие команды, которые могут быть эффективными, но есть один метод, который вы никогда не должны использовать. Не создавайте maintenance team, или bug fixing team, или, в качестве альтернативы, «инкубационную команду», которая создает крутые новые функции. Если вы хотите неустойчивого роста с наибольшим увеличением сложности с течением времени, это путь к такому результату.

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


Рост без подавляющего бремени сложности

Распространенной закономерностью для крупных программных систем является то, что со временем их масштаб увеличивается, что приводит к состоянию, когда они больше не могут быстро развиваться или справляться с регулярной текущей работой. В конце концов они умирают, или кто-то должен перестроить их с нуля. Бизнес или инженерная команда принимают это решение, потому что это кажется самым простым путем вперед. Это не обязательно так, но чтобы избежать этого, необходимо изменить образ мышления. Сосредоточить внимание на снижении сложности и устойчивом росте. Предпочтение отдается улучшению нашей основной функциональности, а не добавлению новых возможностей. Существуют определенные причины для новых функций, они могут быть критически важными для продукта, но мы должны создавать их с осознанием их стоимости. Мы также должны осознавать, что мы празднуем, и создавать вокруг этого стимулы. Придается ли работе по улучшению удобства обслуживания (maintainability), эффективности и существующей функциональности такой же вес, как поставке чего-то нового и блестящего? Я подозреваю, что если бы это было так, у нас было бы более стабильное и надежное программное обеспечение, которое могло бы избежать примера окончательного краха стольких проектов.