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

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

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

Сложность - корень всех зол

Прежде чем идти дальше, важно различать существенную и случайную сложность.

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

Вы не можете избежать этого, но есть кое-что, что может помочь уменьшить его. Отбросьте черты. Упростите проблему.

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

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

Упрощение проблемы означает решение более простой проблемы или ее подмножества. Он может охватывать 90% вариантов использования, но будет в 10 раз проще. Это также относится к предотвращению ползучести объема, расширению масштабов проблемы со временем.

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

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

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

Отдаете приоритет доставке функций?

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

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

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

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

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

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

Сложность ведет к еще большей сложности

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

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

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

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

Локальный против повсеместного

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

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

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

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

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

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

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

Развязка системы

Повсеместная случайная сложность тесно связана с сцеплением. Благодаря разделению модулей взаимозависимость между модулями уменьшается, изменения имеют ограниченный объем в отношении количества модулей, на которые они влияют, и в целом систему легче поддерживать и расширять.

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

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

События спешат на помощь

Мой любимый способ построения разделенных систем - использовать CQRS / ES (разделение ответственности за запросы команд вместе с источником событий). Использование событий для связи между подсистемами и во времени помогает в разделении вещей.

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

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

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

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

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

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

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

дальнейшее чтение