автордың кітабын онлайн тегін оқу Совершенный софт
Переводчик Е. Матвеев
Литературные редакторы М. Макурина, Е. Тихонова
Художник В. Мостипан
Корректоры С. Беляева, Н. Викторова
Джувел Лёве
Совершенный софт. — СПб.: Питер, 2020.
ISBN 978-5-4461-1621-8
© ООО Издательство "Питер", 2020
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Отзывы о книге «Совершенный софт»
Я посещал как мастер-класс для архитекторов, так и мастер-класс по планированию проектов. До этого я почти потерял всякую надежду, что мне когда-нибудь удастся понять, почему работа моей команды никогда не приводит к успешному завершению, и лихорадочно пытался найти работоспособное решение, которое бы остановило нашу безумную гонку на выживание. Мастер-классы открыли передо мной мир, в котором разработка ПО поднимается на уровень всех остальных инженерных дисциплин и организуется профессионально, предсказуемо и надежно, что приводит к разработке высококачественных программных продуктов в рамках срока и бюджета. Полученные знания были бесценными! От создания основательной, проработанной архитектуры, которая может устоять перед вечно изменяющимися требованиям пользователей, до внутренних подробностей планирования и направления проекта к успешному завершению — все это было представлено с непревзойденным профессионализмом и компетентностью. А если учесть, что каждое слово истины, которым Джувел поделился с нами на мастер-классах, было получено, проверено и подтверждено реальной жизнью, этот усвоенный опыт превратился в мощный комплекс знаний, абсолютно необходимый для каждого, кто стремится стать архитектором в области программных систем.
Россен Тотев (Rossen Totev), программный архитектор/руководитель проектов
Мастер-класс по планированию проектов стал событием, изменившим мою карьеру. Я вырос в среде, в которой ограничения по срокам и бюджетам нарушались патологически, так что возможность учиться у Джувела стала даром небес. Шаг за шагом он представлял компоненты и необходимые инструменты для правильного планирования проектов. Это позволяет держать под контролем затраты и сроки в динамической и даже хаотичной среде современной разработки программных продуктов. Джувел говорит, что вы вступаете в асимметричную войну против превышения затрат и сроков, а после его мастер-классов у вас появляется ощущение, что вступаете в рукопашный бой с пистолетом. Никакого волшебства в этом нет — только применение базовых инженерных и производственных доктрин в области ПО, но, вернувшись в офис, вы будете чувствовать себя настоящим волшебником.
Мэтт Роболд (Matt Robold), руководитель разработки ПО, West Covina Service Group
Фантастический опыт. Навсегда изменил мой образ мышления относительно того, как следует подходить к разработке программных продуктов. Я всегда знал, что какая-то часть моих ощущений относительно проектирования и программирования была правильной. Просто я не умел выразить ее словами, зато теперь эти слова у меня появились. Новые знания повлияли на мой подход не только к проектированию программных продуктов, но и к другим видам проектирования.
Ли Мессик (Lee Messick), ведущий архитектор
Программный проект, над которым я работаю, годами страдал от немыслимо жестких сроков. Попытки разобраться в методологиях разработки и сформировать правильный процесс казались напрасной тратой сил, потому что помимо неразумных требований заказчиков приходилось бороться с нежеланием руководства идти на какие-либо компромиссы. Я сражался на двух фронтах, и поражение казалось неизбежным. Мастер-класс предоставил четкое видение ситуации, о котором я никогда не подозревал. Я получил именно те знания, которые искал. Освоил фундаментальные методы, которые изменили мое понимание того, как работают программные проекты. Теперь у меня есть инструменты для эффективного управления моими проектами в потоке бесконечно изменяющихся требований. В мире хаоса этот мастер-класс навел порядок. Я бесконечно благодарен IDesign. Моя жизнь никогда не будет прежней.
Аарон Фридман (Aaron Friedman), архитектор
Жизнь изменилась. Я чувствую себя как хорошо настроенное пианино, на котором уже пару десятилетий копилась пыль.
Джордан Ян (Jordan Jan), технический директор/архитектор
Курсы были просто фантастическими. Пожалуй, это была самая напряженная, но и самая плодотворная неделя моей профессиональной жизни.
Стоил Панков (Stoil Pankov), архитектор
Обучение у Джувела Лёве изменило мою жизнь. Я прошел путь от простого разработчика до настоящего архитектора, применяющего инженерные принципы из других дисциплин к проектированию не только программных продуктов, но и своей карьеры.
Кори Торгерсен (Kory Torgersen), архитектор
Мастер-класс для архитекторов — настоящий урок жизни из области проектирования и квалификации, который я прошел дважды. Знания преподносились на совершенно новом уровне, и я жалею, что не прошел это обучение десятки лет назад, в самом начале своей карьеры. «Перепрошивать мозг» и забывать то, что я знал ранее, неприятно, но я должен был пройти через это. Наконец, каждый последующий день я размышляю над тем, что сказал Джувел на мастер-классе, и пользуюсь новыми знаниями, помогая своей команде даже в мелочах — чтобы со временем все мы могли называть себя Профессиональными Инженерами. (P.S. При втором прохождении я написал более 100 страниц заметок!)
Джейсу Джеячандрат, руководитель разработки ПО, Nielsen
Если вы расстроены, творческой энергии не осталось, если вы чувствуете отчаяние, видя множество неудач в нашей отрасли, этот мастер-класс вдохновит вас. Он поднимет вас на новый уровень профессиональной зрелости, а также придаст надежды и уверенности в том, что вы сможете правильно применить новые знания. Вы выйдете с мастер-класса по планированию проектов с новым образом мышления и достаточным количеством полезных инструментов, с которыми любая неудача программного проекта станет непростительной. Вы тренируетесь, решаете практические задачи, получаете представление о сути вещей и получаете опыт. Да, когда приходит время сообщать ключевым участникам данные о затратах, времени и риске проекта, МОЖНО быть точным. Не ждите, пока ваша компания отправит вас на этот мастер-класс. Если вы серьезно относитесь к своей карьере, поскорее запишитесь на этот или любой другой мастер-класс IDesign. Это лучшее вложение в свою карьеру, которое можно сделать. Спасибо всей команде IDesign за ее непрестанную работу по превращению отрасли разработки в серьезную инженерную дисциплину.
Люциан Мариан (Lucian Marian), архитектор, Mirabel
Как человек, который находится в начале своей карьеры, я могу честно заявить, что этот учебный курс изменил мою жизнь и мое отношение к работе. Я искренне считаю, что он стал поворотной точкой моей жизни.
Алекс Карпович (Alex Karpowich), архитектор
Хочу поблагодарить вас за неделю, изменившую мою (профессиональную) жизнь. Обычно я не могу высидеть на учебных курсах более 50% времени — они занудны и не учат меня ничему, чего бы я не знал или не мог узнать сам. На мастер-классе для архитекторов на протяжении 9 часов каждый день мне не было скучно: я узнал о своих обязанностях как архитектора (я-то думал, что архитектор — всего лишь проектировщик программных продуктов), об инженерном аспекте разработки, о важности реализации не только к заданному времени, но и в рамках бюджета и качества. Я научился не ждать, пока из меня «вырастет» архитектор, а активно управлять своей карьерой, научился оценивать и давать объективную оценку тому, что прежде считал интуитивными инсайтами. После этой недели многое встало на свои места. Не могу дождаться следующего мастер-класса.
Итай Золберг (Itai Zolberg), архитектор
Моему отцу Томасу Чарльзу (Томми) Лёве
Предисловие
Вряд ли хоть кто-то из нас пришел в разработку программного обеспечения не по своей воле. Многие буквально влюбляются в программирование и решают, что хотят зарабатывать им на жизнь. Тем не менее между радужными представлениями о карьере и темной, угнетающей реальностью разработки лежит пропасть. Отрасль программирования в целом переживает глубокий кризис. Этот кризис оказался особенно острым из-за своей многомерности; нарушен буквально каждый аспект разработки:
• Затраты. Бюджет, выделенный для проекта, слабо связан с фактическими затратами на разработку системы. Многие организации даже не пытаются решить проблему с затратами — возможно, потому, что просто не знают, как к ней подойти, или потому, что это заставит их признать, что работа над системой окажется экономически неоправданной. Даже если затраты на первую версию новой системы будут оправданны, часто затраты на протяжении срока жизни системы намного выше положенных из-за некачественного проектирования и неспособности адаптироваться к изменениям. Со временем затраты на сопровождение становятся настолько неприемлемыми, что компании обычно решают начать с чистого листа — только для того, чтобы в ближайшем будущем получить не менее, а то и более дорогостоящую кучу хлама вместо новой системы. Ни в одной другой отрасли не встречается регулярный перезапуск систем просто из-за того, что поддержка старой системы экономически нецелесообразна. Авиакомпании эксплуатируют лайнеры десятилетиями, а жилой дом может быть построен сотню лет назад.
• График. Дедлайны часто оказываются взятыми из головы и являются ничем не обеспеченными конструкциями, так как не имеют никакого отношения ко времени, которое реально потребуется для разработки системы. Для большинства разработчиков дедлайны — всего лишь бесполезные ориентиры, которые проносятся мимо в процессе работы. Если команда разработки выдерживает дедлайн, все удивляются, ибо всегда ждут, что сроки будут сорваны. И это тоже является прямым результатом плохого проектирования системы, что приводит к каскадному распространению в ней новых изменений и новой работы, с которой ранее завершенная работа становится недействительной. Более того, это является результатом чрезвычайно неэффективного процесса разработки, который не учитывает зависимости между активностями и не пытается отыскать самый быстрый и безопасный способ построения системы. Мало того что время реализации всей системы становится неприемлемо большим — и время реализации отдельных функций тоже может увеличиваться. Нарушение сроков в проекте — достаточно плохо; но еще хуже, когда нарушение скрывается от руководства и заказчиков, потому что никто не имеет ни малейшего понятия об истинном состоянии проекта.
• Требования. В конечном итоге часто оказывается, что разработчики решают не те задачи. Между заказчиками или их внутренними посредниками (например, отделом маркетинга) и группой разработки существует постоянное недопонимание. Также многие разработчики не признают свою неспособность сохранить суть требований. Даже идеально переданные требования с большой вероятностью со временем изменятся. Такие изменения делают решение недействительным и ставят под удар все, что пыталась построить команда.
• Персонал. Даже среднестатистические программные системы настолько сложны, что человеческий мозг просто не способен в них полностью разобраться. Внутренняя и внешняя сложность является прямым результатом неудачной архитектуры системы; в свою очередь, она ведет к появлению запутанных систем, создающих массу сложностей в сопровождении, расширении и повторном использовании.
• Сопровождение. Часто сопровождением программных систем занимаются совсем не те люди, которые их разрабатывали. Новые сотрудники не понимают, как работает система, и в результате попытки решения старых проблем постоянно приводят к созданию новых. Все это ведет к стремительному росту затрат на сопровождение и времени вывода продукта на рынок, а также к отмене проектов или их перезапуску с чистого листа.
• Качество. Возможно, ничто не нарушается в программных системах так серьезно, как качество. В программных продуктах есть баги, и можно сказать, что они стали неотъемлемой частью разработки. Говоришь «ПО» — имеешь в виду «баги». Разработчики не могут представить себе программную систему, избавленную от багов. Исправление дефектов (как и добавление новых функций или просто сопровождение) часто увеличивает счетчик ошибок. Плохое качество является прямым результатом малопонятной системной архитектуры, которая создает проблемы с тестированием и сопровождением. Не менее важно то, что многие проекты не учитывают важнейшие активности по контролю качества и не выделяют достаточно времени для того, чтобы каждая активность была завершена идеально.
Несколько десятилетий назад в отрасли начали разрабатываться продукты для решения мировых проблем. В наши дни разработка сама по себе стала проблемой мирового уровня. Проблемы разработки часто проявляются в нетехнических аспектах: рабочих средах с высоким уровнем стресса, высокой текучести кадров, нервном истощении, отсутствии доверия, низкой самооценке и даже в соматических заболеваниях.
Ни одна из проблем разработки не нова1. Некоторые люди за всю свою карьеру в разработке ни разу не видели, чтобы продукт был сделан правильно. Они начинают верить, что это в принципе невозможно, и отвергают любые попытки решения проблем, потому что «уж так сложилось». Они даже могут оказывать сопротивление людям, пытающимся улучшить процесс разработки. Они уже решили для себя, что цель недостижима, поэтому каждый, кто пытается добиться лучших результатов, желает невозможного, а это оскорбляет их самолюбие.
Мой опыт является контрпримером, который доказывает, что успешная разработка программных систем возможна. Каждый проект, за который я отвечал, выпускался в пределах срока, бюджета и без дефектов. История продолжилась и после основания компании IDesign, в которой мы помогаем своим клиентам снова и снова успешно выполнять свои обязательства.
Эта последовательная, систематичная история успеха не была случайна. Я получал образование в области проектирования систем — как физических, так и программных, поэтому было нетрудно узнать сходство этих двух миров. Применение практических принципов — идей, которые считаются проявлением здравого смысла в других инженерных областях, — было оправданно и в области программных систем. Мне никогда не приходило в голову рассматривать разработку программного продукта как техническую задачу или разрабатывать систему без плана. Я не видел смысла идти на компромиссы со своими убеждениями или поступаться принципами, потому что правильный подход работал, а устрашающие последствия отхода от него были очевидны. Мне повезло с учителями; я оказался в нужном месте в нужное время, видел, что работает, а что не работает; мне представилась возможность поучаствовать в больших важных проектах и быть частью культуры высших достижений.
В последние годы я заметил, что проблемы отрасли усугубляются. Все больше программных проектов завершаются неудачей. Эти неудачи обходятся все дороже по времени и деньгам, и даже завершенные проекты обычно отклоняются от своих исходных обязательств. Кризис усугубляется не только тем, что системы становятся все больше, не только жесткими сроками или более высокой частотой изменений. Подозреваю, настоящая причина заключается в том, что знания о том, как правильно проектировать и планировать программные системы, медленно уходят из команд разработчиков. Когда-то в большинстве команд присутствовал ветеран, который обучал молодых и передавал коллективные знания. В наши дни эти учителя перешли на другую работу или вышли на пенсию. В их отсутствие рядовые сотрудники располагают бесконечным количеством информации при нуле знаний.
Я бы очень хотел, чтобы кризис в области программирования мог быть решен каким-то одним способом: применением процесса, методологии разработки, инструмента или технологии. К сожалению, для решения многомерной задачи требуются многомерные решения. В этой книге предлагается комплексное решение: исправление процесса разработки.
По сути, все, что я предлагаю, — проектирование и разработка программных систем с применением инженерных принципов. К счастью, нет необходимости заново изобретать велосипед. Другие инженерные дисциплины были достаточно успешны, поэтому отрасль разработки программного обеспечения может позаимствовать ключевые универсальные принципы проектирования и адаптировать их для программных систем и проектов. Чтобы добиться успеха, необходимо принять инженерную точку зрения. Вы хотите, чтобы программная система была простой в сопровождении и расширении, экономичной, пригодной для повторного использования и реализуемой по времени и риску? Все это инженерные, а не технические аспекты. Они напрямую обусловлены проектированием системы и планированием проекта. Появился специальный термин «архитектор программного продукта» (или просто «архитектор»), которым обозначается участник команды, который отвечает за все аспекты проекта, связанные с проектированием. Соответственно я буду называть читателя «архитектором».
Идеи, представленные в книге, не затрагивают всего, что необходимо знать для достижения успеха. Тем не менее они безусловно станут хорошей отправной точкой, так как направлены на исправление корневой причины всех проблем, упоминавшихся ранее. Корневой причиной является некачественное проектирование самой программной системы или планирование проекта, используемого для построения этой системы. Вы увидите, что программный продукт вполне может быть реализован в пределах графика и бюджета и что планировать системы, удовлетворяющие всем мыслимым требованиям, реально. Такие системы также не создают проблем в сопровождении, расширении и повторном использовании. Надеюсь, что использование этих идей выведет на правильный путь не только систему, над которой вы работаете, но и вашу карьеру, и что оно снова разожжет вашу страсть к программированию.
Структура книги
В книге представлен структурированный инженерный подход к проектированию систем и планированию проектов. Методология состоит из двух частей, нашедших отражение в структуре книги: проектирование системы (создание архитектуры) и планирование проекта. Обе части взаимно дополняют друг друга и являются обязательными. Приложения дополняют основной материал.
Обычно в технической литературе каждая глава посвящена отдельной теме. Такой подход упрощает написание книги, но обычно не соответствует особенностям нашего процесса познания. Напротив, в этой книге процесс обучения напоминает спираль. В обеих частях книги в каждой главе мы возвращаемся к идеям предыдущих глав, погружаясь в материал или развивая идеи на основании дополнительной информации, охватывающей разные аспекты. Такая подача материала повторяет естественный процесс обучения. Каждая глава строится на материале предшествующих глав, поэтому читать главы стоит по порядку. В обе части книги включен подробный практический пример, демонстрирующий основные идеи, а также дополнительные аспекты. В то же время, чтобы итерации были более компактными, обычно я стараюсь избегать самоповторений, так что даже ключевые моменты обсуждаются только один раз.
Ниже приведено саммари глав и приложений.
Глава 1. Метод
В главе 1 представлена ключевая идея: чтобы добиться успеха, вы должны спроектировать систему и спланировать проект для ее построения. Обе составляющие крайне важны для успеха. Проект невозможно планировать без архитектуры, а строить систему, которую вы не сможете построить, бессмысленно.
Часть I. Проектирование системы
Глава 2. Декомпозиция
Глава 2 посвящена разложению системы на компоненты, образующие ее архитектуру. Большинство архитекторов выполняет декомпозицию систем худшим из всех возможных способов, поэтому глава начинается с объяснения того, чего делать не следует. Разобравшись с ошибками, вы увидите, как правильно выполнить декомпозицию системы, и освоите простые средства анализа и наблюдения, которые помогут вам в этом процессе.
Глава 3. Структура
Глава 3 развивает идеи главы 2 и дополняет их структурой. Вы узнаете, как сохранять требования, как строить иерархию уровней в архитектуре, освоите таксономию компонентов архитектуры, их взаимоотношения, узнаете конкретные рекомендации по классификации, а также некоторые сопутствующие аспекты, такие как проектирование подсистем.
Глава 4. Композиция
В главе 4 показано, как объединить компоненты системы в композицию, которая удовлетворяет заданным требованиям. В этой короткой главе приводятся некоторые из ключевых принципов книги; она использует материал двух предыдущих глав для создания мощного ментального инструмента, который вы будете применять в любой системе.
Глава 5. Пример проектирования системы
Глава 5 содержит обширный практический пример, который демонстрирует основные идеи проектирования систем, рассматривающиеся до этого момента. Последняя итерация спирали проектирования системы представляет реальную систему, выравнивает проектировочное решение с требованиями бизнеса и показывает, как создать архитектуру и проверить ее на жизнеспособность.
Часть II. Планирование проекта
Глава 6. Мотивация
Так как большинство людей никогда не слышало о планировании проектов (не говоря уже о том, чтобы применять его), в этой главе описана эта концепция и предоставлены побудительные причины для участия в планировании проекта. Это нулевая итерация спирали планирования проектов.
Глава 7. Обзор планирования проекта
В главе 7 приведен общий обзор планирования проектов. Глава начинается с определения успеха при разработке, после чего представляются ключевые концепции обоснованных решений, комплектования проекта, сети проекта, критического пути, сроков и затрат. В главе рассматриваются многие идеи и методы, используемые в последующих главах, а завершается она важным обсуждением ролей и обязанностей.
Глава 8. Сеть и временные резервы
В главе 8 подробно рассматривается сеть проекта и ее использование как инструмента проектирования. Вы увидите, как смоделировать проект в качестве диаграммы сети, узнаете ключевую концепцию временного резерва, научитесь использовать временные резервы при комплектовании и назначении сроков, а также поймете, как временные резервы связаны с риском.
Глава 9. Время и затраты
В главе 9 определяются возможные компромиссы между временем и затратами в любых проектах, а также описываются возможности ускорения проектов за счет чистой и правильной организации работы. Кроме этого, вы познакомитесь с ключевыми концепциями уплотнения, кривой «время-затраты» и составляющими затрат.
Глава 10. Риск
В главе 10 представлен отсутствующий элемент большинства проектов: риск, выраженный в числовой форме. Вы узнаете, как измерить риск, как связать его с концепциями времени и затрат из предыдущей главы и как вычислить риск на основании сети. Риск часто становится лучшим способом оценки вариантов, это первоклассный инструмент планирования.
Глава 11. Планирование проекта в действии
В главе 11 все концепции предыдущих глав реализуются на практике посредством систематического применения шагов, задействованных в планировании проекта. Хотя проект обладает всеми качествами примера, он приведен прежде всего для демонстрации процесса мышления, используемого при планировании проектов, а также подготовки представления проекта ответственным за принятие решений со стороны бизнеса.
Глава 12. Расширенные методы планирования
В соответствии со спиральной моделью обучения в этой главе приводится описание нетривиальных приемов и концепций. Эти методы применяются в проектах разных уровней сложности, от простых до самых сложных. Эти расширенные методы дополняют предыдущие главы и друг друга и часто применяются в сочетании друг с другом.
Глава 13. Пример планирования проекта
В главе 13 рассматривается пример планирования проекта, соответствующего примеру проектирования системы из главы 5. Рассматриваемый проект также является практическим примером, демонстрирующим процесс планирования проекта от начала до конца. Пример занимает в этой главе центральное место, а методам отводится вторичная роль.
Глава 14. Завершение
Последняя глава слегка отступает от технических аспектов планирования. В ней предлагается подборка рекомендаций, советов, точек зрения и идей процесса разработки. Глава начинается с ответа на важный вопрос: когда следует заниматься планированием проекта? А в конце главы рассматривается влияние плана проекта на качество.
Приложения
Приложение А. Отслеживание проекта
Приложение А показывает, как отслеживать прогресс проекта с учетом плана и как принимать меры по исправлению ситуации, когда это потребуется. Отслеживание проекта в большей степени направлено на управление проектом, нежели на планирование проектов, но оно критично для выполнения ваших обязательств после начала работы.
Приложение Б. Проектирование контрактов сервисов
Архитектура сама по себе достаточно широка и неопределенна, и вам придется спроектировать подробности каждого из ее компонентов. Самая важная из этих подробностей — контракты сервисов. В приложении Б обозначен правильный путь проектирования контрактов сервисов. Кроме того, обсуждение модульности, размера и затрат очень хорошо сочетается с большинством глав этой книги.
Приложение В. Стандарт проектирования
В приложении В приведен объединенный список ключевых директив, рекомендаций, а также того, что можно или чего нельзя делать в тех или иных ситуациях. Стандарт предельно лаконичен; в нем можно найти ответы на вопрос «что?», а не «почему?». Обоснования стандарта содержатся в основном тексте книги.
Кто вы?
Хотя книга написана для архитекторов в области разработки программных систем, она имеет намного более широкую аудиторию. Предполагается, что вы — читатель — являетесь архитектором или старшим специалистом в области разработки, менеджером проекта или совмещаете сразу несколько ролей. Разработчики-энтузиасты, желающие повысить свою квалификацию, найдут в книге много полезного. Впрочем, независимо от вашей текущей должности книга откроет перед вами немало дверей на протяжении всей вашей карьеры. Даже если вы не являетесь опытным архитектором, открывая первую страницу книги, после ее прочтения и освоения методологии вы будете стоять наравне со специалистами высочайшего уровня.
Методы и идеи, представленные в книге, актуальны независимо от языка программирования (будь то C++, Java, C# или Python), платформы (Windows, Linux, мобильные, локальные и облачные) и размера проекта (от самых мелких до самых крупных проектов). Также они актуальны для всех отраслей (от здравоохранения до обороны), любых бизнес-моделей и размеров компаний (от начинающих фирм до крупных корпораций).
Самое важное предположение, которое я делаю относительно читателя, — что вам действительно небезразлично то, чем вы занимаетесь, а текущие неудачи и потери вас беспокоят. Вы хотите добиться большего, но не знаете, что делать, или вас сбивают с толку некачественные практики.
Что необходимо для работы с книгой
Единственное обязательное условие — непредвзятый ум. Прошлые неудачи и огорчения весьма желательны.
Дополнительные онлайн-ресурсы
На веб-странице книги представлены файлы примеров, дополнения и исправления ошибок. Страница доступна по адресу
http://www.rightingsoftware.org
Вы найдете файлы и вспомогательный материал для книги по ссылке DownloadSupportFiles.
За дополнительной информацией о книге обращайтесь по адресу
informit.com/title/9780136524038
Также с автором можно связаться по адресу
http://www.idesign.net
Благодарности
Прежде всего, хочу поблагодарить двух друзей, которые убедили меня написать эту книгу, причем каждый по-своему: Гад Меир (Gad Meir) и Яркко Кемппайнен (Jarkko Kemppainen).
Спасибо редактору и по совместительству источнику объективной критики Дэйву Киллиану (Dave Killian): еще немного правки, и мне пришлось бы указывать тебя как соавтора. Спасибо Бет Сайрон (Beth Siron) за рецензирование чернового варианта рукописи. Следующие люди выделили свое время на рецензирование рукописи: Чед Майкл (Chad Michel), Дуг Дархэм (Doug Durham), Джордж Стивенс (George Stevens), Джош Лойд (Josh Loyd), Риккардо Беннет-Ловси (Riccardo Bennett-Lovsey) и Стив Лэнд (Steve Land).
Наконец, я благодарен своей жене Дане, которая вдохновляет меня писать книги и помогает выделить время, освобождая меня от семейных обязанностей. И спасибо моим родителям, привившим мне любовь к техническим дисциплинам.
1 Edsger W. Dijkstra. The Humble Programmer: ACM Turing Lecture. Communications of the ACM 15, no. 10 (October 1972): 859–866. (Дейкстра Эдсгер. Смиренный программист.)
Edsger W. Dijkstra. The Humble Programmer: ACM Turing Lecture. Communications of the ACM 15, no. 10 (October 1972): 859–866. (Дейкстра Эдсгер. Смиренный программист.)
Ни одна из проблем разработки не нова1. Некоторые люди за всю свою карьеру в разработке ни разу не видели, чтобы продукт был сделан правильно. Они начинают верить, что это в принципе невозможно, и отвергают любые попытки решения проблем, потому что «уж так сложилось». Они даже могут оказывать сопротивление людям, пытающимся улучшить процесс разработки. Они уже решили для себя, что цель недостижима, поэтому каждый, кто пытается добиться лучших результатов, желает невозможного, а это оскорбляет их самолюбие.
Об авторе
Джувел Лёве, основатель IDesign, — старший архитектор программных систем, специализирующийся на проектировании систем и планировании проектов. Помогал бесчисленным компаниям по всему миру создавать качественные продукты в рамках сроков и бюджета. Признанный компанией Microsoft как один из ведущих экспертов и отраслевых лидеров, он принимал участие во внутреннем стратегическом анализе архитектуры C#, WCF и других сопутствующих технологий и был прозван «легендой от программирования». Опубликовал несколько книг и множество статей практически по всем аспектам современной разработки. Лёве часто выступает на крупных международных конференциях разработчиков и проводит мастер-классы по всему миру. Он обучает тысячи профессионалов навыкам, необходимым для современных архитекторов программных систем, и лидерству, нужному в проектировании, процессах и технологиях.
От издательства
Цветные версии рисунков вы можете посмотреть, отсканировав QR-код.
Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com (издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.
Для начинающего архитектора существует множество вариантов решения практически любой задачи.
Для опытного архитектора хороших вариантов совсем немного.
1. Метод
«Дзен архитектора»2, по сути, гласит, что для начинающего архитектора существует множество вариантов решения практически любой задачи. Однако для опытного архитектора хороших вариантов совсем немного — как правило, всего один.
Начинающие архитекторы часто приходят в замешательство от изобилия паттернов, идей, методологий и возможностей проектирования их программных систем. В отрасли разработки полным-полно идей и людей, которые желают учиться и самосовершенствоваться, — включая вас, читателя этой книги. Но поскольку существует лишь несколько правильных подходов к любой задаче по проектированию, с таким же успехом вы можете сосредоточиться на них и не обращать внимания на посторонний шум. Мастера программной архитектуры знают, как это делается; словно под воздействием какого-то сверхъестественного вдохновения, они немедленно проникают в суть дела и выдают правильно спроектированное решение.
«Дзен архитектора» применим не только к архитектуре систем, но и к проекту, в котором она строится. Да, существует бесчисленное множество способов структурирования проекта и распределения работы между членами команды, но все ли они в равной степени безопасны, быстры, экономичны, полезны, эффективны и действенны? Опытный архитектор также планирует проект для построения системы и даже помогает руководству принять исходное решение о том, может ли оно вообще себе позволить этот проект.
Истинное мастерство в любой области — это путь. Никто не рождается экспертом (за редчайшим исключениями). Моя карьера наглядно свидетельствует об этом. Я начал работать младшим архитектором почти 30 лет назад, когда сам термин «архитектор» еще не так часто встречался в фирмах, занимающихся разработкой. Сначала я перешел на роль архитектора проекта, потом на роль архитектора подразделения, а к концу 1990-х стал ведущим архитектором программных систем в компании из Кремниевой долины из списка Fortune 100. В 2000 году я основал IDesign — компанию, чья деятельность посвящена исключительно проектированию программных систем. С тех пор в IDesign были спроектированы сотни систем и проектов. Хотя в каждом случае существовала конкретная архитектура и план проекта, независимо от заказчика, проекта, системы, технологии или состава разработчиков мои рекомендации по проектированию, по сути, оставались одними и теми же.
И тогда я задал себе простой вопрос: действительно ли необходимо быть опытным архитектором с десятилетиями опыта практического проектирования программных систем и десятками проектов за плечами, чтобы выбрать правильный подход? Или возможно структурировать проект так, чтобы любой человек с четким пониманием используемой методологии смог предложить достойный вариант дизайна системы и проекта?
На второй вопрос я отвечаю решительным «да». Результат, который я называю «Методом», и является темой этой книги. В результате применения Метода во множестве разных проектов, после обучения и курирования нескольких тысяч архитекторов по всему миру я убедился в том, что при правильном применении он работает. При этом я не умаляю ценности правильного отношения, технической квалификации и аналитических способностей — все это необходимые ингредиенты успеха независимо от используемой методологии. К сожалению, этих ингредиентов недостаточно; я часто вижу, как проекты проваливаются, хотя их разработчики обладают всеми замечательными качествами и атрибутами. Однако в сочетании с Методом эти ингредиенты дают шанс на успех. Если ваш дизайн будет базироваться на надежных инженерных принципах, вы научитесь держаться подальше от порочных практик и ложных интуитивных представлений, которые так часто принимают за житейскую мудрость.
Что такое «Метод»?
Метод — простая и эффективная система анализа и проектирования. Суть Метода можно выразить формулой:
Метод = проектирование системы + планирование проекта.
В том, что касается проектирования системы, Метод закладывает основу для разбиения большой системы на меньшие модульные компоненты. Метод предоставляет рекомендации по выбору структуры, роли и семантики компонентов и по организации взаимодействий между ними. Результат представляет собой архитектуру системы.
При планировании проекта Метод помогает сформулировать различные варианты построения системы. Каждый вариант представляет собой сочетание графика, затрат и рисков. Также каждый вариант определяет набор инструкций по сборке системы и готовит проект к выполнению и дальнейшему отслеживанию.
Планирование проекта, рассматриваемое в части II книги, является намного более важным фактором успеха, чем проектирование системы. Даже посредственно спроектированная система может оказаться успешной, если проект располагает достаточным временем и ресурсами и если риск находится на приемлемом уровне. При этом систему, спроектированную на первоклассном уровне, ждет провал, если у проекта не хватит времени или ресурсов для ее построения или если проект окажется слишком рискованным. Планирование проекта также намного сложнее проектирования системы и, как следствие, требует большего арсенала инструментов, идей и приемов.
Поскольку Метод сочетает проектирование системы с планированием проекта, фактически он может рассматриваться как процесс проектирования. За прошедшие годы отрасль разработки уделяла огромное внимание процессу разработки и намного меньшее — процессу проектирования. Эта книга призвана заполнить пробел.
Проверка результата проектирования
Проверка результата проектирования также чрезвычайно важна — организация не может рисковать, поручая команде начать разработку непроработанной архитектуры или системы, на построение которой может не хватить средств. Метод оказывает содействие и поддержку в решении этой критически важной задачи, позволяя архитектору утверждать с разумной степенью уверенности, что предложенное решение адекватно, то есть что оно удовлетворяет двум ключевым целям. Во-первых, решение должно удовлетворять потребности заказчика. Во-вторых, оно должно соответствовать возможностям и ограничениям организации или команды.
Когда команда займется программированием, изменение архитектуры часто становится неприемлемым из-за возможных последствий для затрат и графика. На практике это означает, что без проверки результата проектирования системы возникает риск закрепления архитектуры в лучшем случае несовершенной, а в худшем — безобразной. Организации придется каким-то образом жить с полученной системой в течение нескольких следующих лет и нескольких версий до следующей крупной переработки. Плохо спроектированная программная система может серьезно повредить бизнесу, лишив его возможности реагировать на коммерческие возможности и даже причиняя серьезный финансовый ущерб из-за растущих затрат на сопровождение.
Ранняя проверка результата проектирования — первоочередная необходимость. Например, если через три года после начала работы выяснится, что конкретная идея или вся архитектура неверна, это будет теоретически интересно, но практическая ценность такого открытия равна нулю. В идеальном случае через неделю после начала проекта вы должны знать, выдержит архитектура или нет. Любая задержка только повысит риск ведения разработки с сомнительной архитектурой. В следующих главах подробно описано, как проверить результат проектирования системы.
Обратите внимание: я говорю именно о проектирования системы, о ее архитектуре, а не о детально разработанном плане. Детально разработанный план определяет для каждого компонента архитектуры ключевые артефакты реализации (интерфейсы, иерархии классов и контракты данных). Такие планы дольше строятся, создаются в ходе исполнения проекта и могут изменяться по мере построения или эволюции системы.
Аналогичным образом необходимо проверять результат планирования проекта. Срыв графика или бюджета (или и того и другого) на середине работы попросту недопустим. Если вы не сможете выполнить свои обязательства, это плохо отразится на вашей карьере. Следует заблаговременно проверить план своего проекта и убедиться в том, что имеющаяся команда способна успешно завершить работу над проектом.
Кроме формирования архитектуры и планов проекта, одной из целей Метода является устранение рисков для проекта, обусловленных проектированием. Проект не должен провалиться только из-за того, что архитектура оказалась слишком сложной для построения и сопровождения. Метод выявляет архитектуру действенно и эффективно и делает это за короткий период времени. То же можно сказать о планировании проекта: проект не должен завершиться неудачей, потому что для него изначально было выделено недостаточно времени или ресурсов. Эта книга показывает, как точно вычислить продолжительность проекта и затраты на него и как принимать обоснованные решения.
Аврал
При использовании Метода можно спроектировать систему за считаные дни, обычно от 3 до 5 дней; примерно столько же времени уйдет на планирование проекта. С учетом высоких целей (а именно формирования архитектуры системы и проектного плана новой системы) это может показаться недостаточным. У типичных бизнес-систем возможность перепроектирования обычно представляется только через несколько лет, так почему бы не потратить 10 дней на архитектуру? На фоне жизненного цикла системы, измеряемого в годах, пять лишних дней даже не назовешь ошибкой округления. Тем не менее дополнительное время проектирования часто не только не улучшает результат, но даже оказывает отрицательное воздействие.
Во многих рабочих средах планирование времени организовано ужасающе неэффективно, прежде всего из-за человеческой природы. Аврал, или кранч, заставляет вас (и других причастных) сконцентрироваться, выбрать приоритеты и сформировать результат. Применять Метод следует быстро и решительно.
В общем случае проектирование занимает немного времени (по сравнению с реализацией). Строительные архитекторы берут почасовую оплату и часто работают над проектированием дома не более одной-двух недель. На строительство дома по проекту архитектора может уйти два-три года мучительной работы с подрядчиками, но архитектору на разработку архитектуры много времени не потребовалось.
Аврал также помогает избежать «украшательства» проекта. Закон Паркинсона3 гласит, что работа заполняет все отпущенное на нее время. Если на проектирование, которое может быть выполнено за 5 дней, будет отведено 10 дней, то, скорее всего, архитектор проработает над ним все 10 дней. Лишнее время будет потрачено на проработку второстепенных аспектов, которые не добавляют ничего, кроме сложности; это обернется непропорциональным повышением затрат на реализацию и сопровождение в предстоящие годы. Ограничение времени проектирования заставляет вас выдать достаточно хорошее решение.
Преодоление аналитического паралича
Аналитический паралич — ситуация, в которой человек (или команда), способный, умный и даже работящий (как и большинство архитекторов программных систем), застревает в бесконечном цикле анализа, проектирования, инсайтов и возврата к продолжению анализа. Работа человека (или команды) фактически парализуется, и ни о каком результативном труде не может быть и речи.
Дерево решений
Состояние паралича возникает прежде всего из-за незнания дерева решений как для проектирования системы, так и для планирования проекта. Дерево решений — общая концепция, применимая ко всем задачам проектирования, а не только в программировании. Проектирование любой сложной сущности состоит из множества малых проектировочных решений, выстроенных иерархически в древовидную структуру. Каждое разветвление в дереве представляет отдельный путь, приводящий к новым, более детализированным решениям из области проектирования. Листья дерева представляют разные проектировочные решения для действующих требований. Каждый лист — отдельное, целостное и действительное решение, чем-то отличающееся от всех остальных листьев.
Если человек или команда, ответственные за разработку проектировочного решения, плохо представляют правильное дерево решения, то начинают не от корня дерева, а с другого места. В какой-то момент последующее решение неизбежно нивелирует предыдущее; все решения, принятые между этими двумя точками, станут недействительными. Такое проектирование напоминает пузырьковую сортировку дерева решений. Так как количество операций при пузырьковой сортировке приблизительно равно квадратному корню количества элементов, потери будут значительными. Простая программная система, требующая приблизительно 20 решений из области проектирования системы и планирования проекта, потенциально может потребовать около 400 итераций, если вы не следуете дереву решений. Проведение такого количества встреч (даже распределенных по времени) приведет к параличу. Вряд ли у вас найдется время хотя бы для 40 итераций. Когда время, выделенное на проектирование и планирование, подойдет к концу, разработка начнется в незрелом состоянии системы и проекта. При этом находки, из-за которых решения из области проектирования станут недействительными, будут отложены до еще более худшего момента в будущем, когда с неправильными решениями уже будут связаны время, усилия и артефакты. Потери от ошибочных решений будут максимизированы.
Дерево проектировочных решений программной системы
Как выясняется, у многих программных бизнес-систем есть много общего, и по крайней мере контуры дерева решений у таких систем не только похожи, но и даже унифицированы. Естественно, листья у них различаются.
Метод предоставляет дерево решений для типичной бизнес-системы — как для проектирования системы, так и для планирования проекта. Только после того, как вы спроектируете систему, появляется смысл планирования проекта для построения этой системы. У каждой из этих областей — проектирования системы и планирования проекта — имеется собственное поддерево проектировочных решений. Метод будет руководить вашими перемещениями по дереву, начиная от корня, что позволит избежать переделок и переоценки предыдущих решений.
Один из самых полезных приемов усечения дерева решений — применение ограничений. Как подметил Фредерик Брукс4, вопреки здравому смыслу или интуиции, худшая задача по проектированию — это чистый холст. Без ограничений процесс проектирования должен быть простым, верно? Нет. Чистый холст приведет в ужас любого архитектора. Существует бесконечное множество способов решить задачу неправильно или нарушить скрытые ограничения. Чем больше ограничений, тем проще задача проектировщика. Чем меньше свободы действий, тем более очевиден результат проектирования. В полностью ограниченной системе проектировать нечего: все устроено совершенно определенным образом. Так как при проектировании всегда существуют ограничения (явные или неявные), при отработке дерева проектировочных решений Метод устанавливает последовательно нарастающие ограничения для системы и проекта вплоть до точки, в которой начинается быстрое схождение и окончательное формирование результата.
Обмен информацией
Важным преимуществом Метода является обсуждение идей, относящихся к проектированию. После того как участники будут знакомы с устройством архитектуры и семантикой проектирования, Метод предоставляет возможность обмениваться идеями и точно передавать информацию о том, чего требует. Вы можете представить команде свой процесс мышления, лежащий в основе проектирования. Делитесь различными решениями и наблюдениями, которыми вы руководствовались при формировании архитектуры, четко и однозначно документируйте свои рабочие версии и полученные в итоге проектировочные решения.
Такой уровень четкости и прозрачности намерений проектировщика абсолютно необходим для жизнеспособной архитектуры. Хорошим решением может считаться то, которое было хорошо продумано, пережило стадию разработки и в итоге было воплощено в виде работающих компонентов на машинах заказчика. Вы должны уметь передавать замысел проектирования разработчикам и следить за тем, чтобы они серьезно относились к намерениям и концепциям, лежащим в его основе. Концепция проектирования должна подкрепляться анализом, исследованиями и курированием. Метод прекрасно проявляет себя с коммуникациями такого типа вследствие сочетания четко определенной семантики и структуры.
Не сомневайтесь: если разработчики, которым поручено построение системы, не понимают и не ценят ее проектное решение, они ее угробят. Сколько бы времени ни было потрачено на критический анализ кода или проекта, это не изменит ситуации. Целью критического анализа должно быть выявление непреднамеренных отклонений от архитектуры на как можно более ранней стадии.
Все сказанное остается истинным и тогда, когда наступает время передать план проекта руководителям проекта, руководству или другим ключевым участникам. Четкие, однозначные, объективно сопоставимые варианты — ключ к обоснованным решениям. Когда люди принимают неверные решения, часто это происходит из-за того, что они не понимают проект и неверно представляют себе его поведение. Строя правильные модели для проекта в отношении времени, затрат и рисков, архитектор делает возможным принятие правильных решений. Метод предоставляет правильную терминологию и метрики для простого, конкретного общения с ответственными за принятие решений. После того как руководители осознают возможности проектного решения, они станут его главными сторонниками и будут настаивать на том, чтобы работа велась именно так, а не иначе. Никакие вдохновенные речи не сравнятся по эффективности с простым набором диаграмм и чисел. Более того, план проекта важен не только на начальной стадии. В процессе работы вы можете пользоваться средствами планирования проектов для того, чтобы сообщать руководству о действенности и целесообразности изменений. Отслеживание проекта и управление изменениями рассматриваются в приложении А.
Помимо передачи информации о проектировочном решении разработчикам и руководству, Метод позволяет архитектору точно и легко сообщать информацию другим архитекторам. Информация, которая может быть получена в ходе критического анализа и рецензирования такого рода, бесценна.
Чем Метод не является
В 1987 году Брукс написал: «Серебряной пули не существует»5. И конечно, Метод ею не является. Применение Метода не гарантирует успеха и может только усложнить ситуацию, если Метод будет использоваться в отрыве от других аспектов проекта или просто ради самого процесса.
Метод не исключает творческого подхода со стороны архитектора и усилий по построению подходящей архитектуры. Архитектор все равно обязан проработать требуемое поведение системы. Архитектор все равно несет ответственность за неправильную реализацию архитектуры, или за то, что информация о ней не была донесена до разработчиков, или за то, что пока он направлял процесс разработки до сдачи продукта, ему не удалось избежать нарушений архитектуры — пусть и в условиях нарастающего давления. Более того, как показано в части II книги, архитектор должен выдать работоспособный план проекта, созданный на основе архитектуры. Архитектор должен произвести калибровку проекта с учетом доступных ресурсов, задействованных рисков и сроков. Выполнять планирование проекта просто ради ритуала бессмысленно. Архитектор должен устранить все смещения и выдать правильный набор исходных предпосылок планирования и оценок результатов.
Метод может предоставить хорошую отправную точку для проектирования системы и планирования проекта, а также список факторов, которых следует избегать. Однако Метод работает только в том случае, если вы добросовестно применяете его, не жалея времени и когнитивных усилий для сбора необходимой информации. Вы должны искренне переживать за процесс проектирования и результаты, которые будет получены в результате его применения.
2 https://en.wikipedia.org/wiki/Zen_Mind,_Beginner’s_Mind. (На русском: Сюнрю Судзуки. Сознание дзен, сознание начинающего. — Примеч. ред.)
3 Cyril N. Parkinson, «Parkinson’s Law», The Economist (November 19, 1955).
4 Frederick P. Brooks Jr., The Design of Design: Essays from a Computer Scientist (Upper Saddle River, NJ: Addison-Wesley, 2010) (на русском: Фредерик П. Брукс. Проектирование процесса проектирования: записки компьютерного эксперта. — М.: Вильямс, 2012. — 464 с. — Примеч. ред.).
5 Frederick P. Brooks Jr., “No Silver Bullet: Essence and Accidents of Software Engineering,” Computer 20, no. 4 (April 1987). (Данную статью можно найти в издании: Брукс Фредерик. Мифический человеко-месяц, или Как создаются программные системы. — СПб.: Питер, 2021. — 368 с.: ил. — Примеч. ред.).
Аврал также помогает избежать «украшательства» проекта. Закон Паркинсона3 гласит, что работа заполняет все отпущенное на нее время. Если на проектирование, которое может быть выполнено за 5 дней, будет отведено 10 дней, то, скорее всего, архитектор проработает над ним все 10 дней. Лишнее время будет потрачено на проработку второстепенных аспектов, которые не добавляют ничего, кроме сложности; это обернется непропорциональным повышением затрат на реализацию и сопровождение в предстоящие годы. Ограничение времени проектирования заставляет вас выдать достаточно хорошее решение.
«Дзен архитектора»2, по сути, гласит, что для начинающего архитектора существует множество вариантов решения практически любой задачи. Однако для опытного архитектора хороших вариантов совсем немного — как правило, всего один.
Один из самых полезных приемов усечения дерева решений — применение ограничений. Как подметил Фредерик Брукс4, вопреки здравому смыслу или интуиции, худшая задача по проектированию — это чистый холст. Без ограничений процесс проектирования должен быть простым, верно? Нет. Чистый холст приведет в ужас любого архитектора. Существует бесконечное множество способов решить задачу неправильно или нарушить скрытые ограничения. Чем больше ограничений, тем проще задача проектировщика. Чем меньше свободы действий, тем более очевиден результат проектирования. В полностью ограниченной системе проектировать нечего: все устроено совершенно определенным образом. Так как при проектировании всегда существуют ограничения (явные или неявные), при отработке дерева проектировочных решений Метод устанавливает последовательно нарастающие ограничения для системы и проекта вплоть до точки, в которой начинается быстрое схождение и окончательное формирование результата.
Frederick P. Brooks Jr., “No Silver Bullet: Essence and Accidents of Software Engineering,” Computer 20, no. 4 (April 1987). (Данную статью можно найти в издании: Брукс Фредерик. Мифический человеко-месяц, или Как создаются программные системы. — СПб.: Питер, 2021. — 368 с.: ил. — Примеч. ред.).
Frederick P. Brooks Jr., The Design of Design: Essays from a Computer Scientist (Upper Saddle River, NJ: Addison-Wesley, 2010) (на русском: Фредерик П. Брукс. Проектирование процесса проектирования: записки компьютерного эксперта. — М.: Вильямс, 2012. — 464 с. — Примеч. ред.).
Cyril N. Parkinson, «Parkinson’s Law», The Economist (November 19, 1955).
https://en.wikipedia.org/wiki/Zen_Mind,_Beginner’s_Mind. (На русском: Сюнрю Судзуки. Сознание дзен, сознание начинающего. — Примеч. ред.)
В 1987 году Брукс написал: «Серебряной пули не существует»5. И конечно, Метод ею не является. Применение Метода не гарантирует успеха и может только усложнить ситуацию, если Метод будет использоваться в отрыве от других аспектов проекта или просто ради самого процесса.
Часть I. Проектирование системы
2. Декомпозиция
Программная архитектура представляет собой высокоуровневый результат проектирования и структуру программной системы. Хотя проектирование системы занимает немного времени и обходится недорого по сравнению с ее построением, исключительно важно правильно реализовать архитектуру. Если после того, как система будет построена, окажется, что архитектура несовершенна, ошибочна или просто не подходит для ваших целей, сопровождение и расширение системы будет обходиться чрезвычайно дорого.
Сущность архитектуры любой системы заключается в разбиении концепции системы как целого (будь то дом, компьютер или программная система) на составляющие. Хороший архитектор также предписывает, как эти компоненты должны взаимодействовать друг с другом во время выполнения. Акт выявления составляющих компонентов системы называется декомпозицией системы.
Правильная декомпозиция исключительно важна для проектирования. Ошибки в ходе декомпозиции приводят к неправильной архитектуре, которая обернется колоссальными проблемами в будущем, а в некоторых ситуациях систему приходится переписывать с нуля.
В прошлом такими структурными элементами были объекты C++, а позднее — компоненты COM, Java или .NET. В современных системах и в этой книге самой детализированной единицей архитектуры является сервис (в сервисно-ориентированном понимании). Однако технология, применяемая для реализации компонентов, и их подробности (интерфейсы, операции и иерархии классов) относятся к подробностям проектирования, а не к декомпозиции системы. Такие подробности могут изменяться, и эти изменения никак не повлияют на декомпозицию и архитектуру.
К сожалению, многие программные системы (а возможно, абсолютное большинство) проектируются неправильно; можно даже сказать, что они спроектированы худшим из всех возможных способом. Дефекты проектирования являются прямым результатом неправильной декомпозиции систем.
По этой причине данная глава начинается с объяснения того, почему некоторые распространенные способы декомпозиции принципиально неверны. Далее приводятся обоснования процесса декомпозиции, заложенного в основу Метода. Также будут представлены некоторые мощные и полезные приемы, которые могут применяться при проектировании системы.
О вреде функциональной декомпозиции
Функциональная декомпозиция разбивает систему на структурные элементы, определяемые функциональностью системы. Например, если система должна выполнять некий набор операций (например, выставление счетов, оплату и поставку товара), вы определяете сервисы Invoicing, Billing и Shipping.
Недостатки функциональной декомпозиции
Функциональная декомпозиция обладает множеством серьезных недостатков. Как минимум функциональная декомпозиция связывает сервисы с требованиями, потому что сервисы отражают требования. Любые изменения в требуемой функциональности приводят к изменению функциональных сервисов. Такие изменения неизбежно возникают с течением времени и приводят к болезненным будущим изменениям в системе, требуя выполнения запоздалой повторной декомпозиции, отражающей новые требования. Помимо дорогостоящих изменений в системе, функциональная декомпозиция препятствует повторному использованию и ведет к появлению слишком сложных систем и клиентов.
Препятствия к повторному использованию
Возьмем простую систему с функциональной декомпозицией, использующую три сервиса, A, B и C, которые вызываются в следующем порядке: сначала A, затем B, и затем C. Так как функциональная декомпозиция также совпадает с декомпозицией по времени (сначала A, потом B), она фактически препятствует отдельному повторному использованию сервисов. Предположим, другой системе также необходим сервис B (допустим, Billing). В основу сервиса B встроено представление о том, что он вызывается после A, но до C (например, сначала выставляется счет, затем производится оплата, и только после этого происходит отгрузка). Любая попытка взять сервис B из первой системы и перенести его во вторую систему завершится неудачей, потому что во второй системе никто не выполняет A до, а C после B. При перенесении B к нему привязываются сервисы A и C. B не является независимым сервисом, пригодным для повторного использования, — A, B и C образуют конгломерат из тесно связанных сервисов.
Слишком много или слишком мало
Один из способов выполнения функциональной декомпозиции — создание сервисов для всех разновидностей функциональности. Этот способ декомпозиции приводит к взрывному росту количества сервисов, так как система сколько-нибудь приличного размера может содержать сотни видов функциональности. Вы не только получите слишком много сервисов, но и в этих сервисах часто будет дублироваться большой объем общей функциональности, адаптированной для конкретного случая. Размножение сервисов приводит к непропорционально высоким затратам на интеграцию и тестирование, а также к повышению общей сложности.
Другой способ функциональной декомпозиции основан на объединении всех возможных способов выполнения операций в «мегасервисы». Это приводит к разбуханию сервисов, в результате чего они становятся слишком сложными, а их сопровождение — практически невозможным. Такие монолиты превращаются в уродливые свалки для всех взаимосвязанных разновидностей исходной функциональности вместе с неочевидными отношениями внутри сервисов и между ними.
Таким образом, функциональная декомпозиция часто ведет к тому, что сервисы становятся слишком большими (тогда их слишком мало) или слишком мелкими (тогда их слишком много). Обе напасти нередко встречаются одновременно в одной системе.
ПРИМЕЧАНИЕ В приложении Б, посвященном проектированию контрактов сервисов, более подробно обсуждаются неприятные последствия от слишком малого или слишком большого количества сервисов, а также от их влияния на проект.
Разбухание клиентов и связи
Функциональная декомпозиция часто приводит к уплощению системной иерархии. Поскольку каждый сервис или структурный элемент посвящен конкретной функциональности, кто-то должен объединить эти разрозненные функциональности в требуемое поведение. Этим «кем-то» нередко становится клиент. Когда клиент координирует работу сервисов, вы получаете плоскую систему из двух уровней: клиенты и сервисы; все промежуточные прослойки при этом исчезают. Допустим, ваша система должна выполнять три операции (или области функциональности): A, B и C, именно в таком порядке. Как показано на рис. 2.1, клиент должен «сшить» эти сервисы в единое целое.
Набивая клиент логикой координации, вы загрязняете клиентский код бизнес-логикой системы. Роль клиента уже не ограничивается вызовом операций в системе или представлением информации для пользователей. Клиент теперь
Рис. 2.1. Координация функциональности из раздувшегося клиента
должен знать все внутренние сервисы до мелочей: как вызывать их, как обрабатывать ошибки, как компенсировать сбой B после успеха A и т.д. Вызов сервисов почти всегда осуществляется синхронно, потому что клиент идет по ожидаемой последовательности «A, потом B, потом C», и другим способом было бы трудно обеспечить последовательность вызовов и обеспечить быструю реакцию на внешние события. Более того, клиент теперь жестко связывается с необходимой функциональностью. Любые изменения в операциях (скажем, вызов B’ вместо B) должны быть отражены в клиенте. В идеале клиент и сервисы должны быть способны эволюционировать независимо друг от друга. Десятилетия назад программисты обнаружили, что включение бизнес-логики на стороне клиента почти всегда нежелательно. Тем не менее при таком проектировании, как на рис. 2.1, вы вынуждены загрязнять клиента бизнес-логикой соблюдения последовательности, упорядочения, компенсации ошибок и продолжительности вызовов. В конечном счете клиент перестает быть клиентом — он становится системой.
А что, если разнотипные клиенты (например, толстые (rich) клиенты, веб-страницы, мобильные устройства) пытаются вызвать одну и ту же последовательность функциональных сервисов? Вам неизбежно придется дублировать логику между клиентами, что сделает их сопровождение слишком неэффективным и дорогостоящим. С изменением функциональности вам придется синхронизировать изменения между разными клиентами, поскольку они отразятся на всех клиентах. Часто в таких ситуациях разработчики стараются избежать любых изменений в функциональности сервисов из-за каскадного воздействия на клиентов. Если вы создали множество клиентов, каждый из которых имеет собственную версию последовательности вызовов, адаптированную для его потребностей, с изменением или заменой сервисов возникает еще больше проблем, что препятствует повторному использованию поведения между клиентами. По сути, приходится заниматься сопровождением нескольких сложных систем, пытаясь синхронизировать их. В конечном итоге это приводит как к торможению нововведений, так и к затягиванию выпуска новой версии при принудительном введении изменений в разработку и производство.
В качестве иллюстрации проблем функциональной декомпозиции, рассмотренных выше, возьмем рис. 2.2. На нем представлена схема анализа цикломатической сложности одной системы, которой я занимался. В качестве методологии проектирования была избрана функциональная декомпозиция.
Рис. 2.2. Анализ сложности результатов функционального проектирования
Цикломатическая сложность оценивает количество независимых путей в коде класса или сервиса. Чем сложнее устроены и теснее связаны между собой внутренние механизмы, тем выше показатель цикломатической сложности. Программа, использованная для построения рис. 2.2, измерила и построила оценки для различных классов в системе. Чем сложнее класс, тем он больше и темнее на этой схеме. С первого же взгляда мы видим три очень больших и очень сложных класса. Насколько легко будет сопровождать MainForm? Что это — форма, элемент пользовательского интерфейса, канал для взаимодействия пользователя с системой или целая система? Обратите внимание на сложность, необходимую для настройки MainForm, — она отражена в размере и цвете FormSetup. Не отстает от них и класс Resources — изменение ресурсов, используемых в MainForm, оказывается очень сложным делом.
В идеале класс Resources устроен тривиально: он должен представлять собой простые списки изображений и строк. Остальная часть системы строится из десятков меньших простых классов, каждый из которых посвящен конкретной функциональности. Меньшие классы буквально оказались в тени трех огромных классов. Тем не менее, хотя каждый из этих меньших классов может быть тривиальным, огромное количество меньших классов само по себе создает проблему сложности из-за тонкостей интеграции между всеми классами. В результате мы имеем и слишком мелкие компоненты, и слишком крупные компоненты, и разбухший клиент.
Множество точек входа
Еще один недостаток декомпозиции на рис. 2.1 — необходимость множества точек входа в систему. Клиент (или клиенты) должен входить в систему в трех местах: для A, для B и для C. Это означает, что вам придется в нескольких местах беспокоиться об аутентификации, авторизации, масштабируемости, управлении экземплярами, распространении транзакций, идентификации, размещении и т.д. Если вам понадобится изменить реализацию любого из этих аспектов, придется изменять ее в разных местах по всем сервисам и клиентам. Со временем из-за этих многочисленных изменений добавление новых клиентов будет обходиться очень дорого.
Разбухание сервисов и связи
В качестве альтернативы последовательности функциональных сервисов на рис. 2.1 можно рассмотреть то, что на первый взгляд является меньшим злом: функциональные сервисы вызывают друг друга, как показано на рис. 2.3.
У такого подхода есть преимущество: клиенты получаются простыми и даже асинхронными. Клиент выдает вызов сервиса A; затем сервис A вызывает B, а B вызывает C.
Рис. 2.3. Цепочки функциональных сервисов
Но теперь появляется другая проблема: функциональные сервисы тесно связаны друг с другом и с порядком функциональных вызовов. Например, сервис оплаты может вызываться только после сервиса выставления счета, но до сервиса отгрузки. В случае рис. 2.3 в сервис A встраивается информация о том, что он должен вызвать сервис B. Сервис B может вызываться только после сервиса A и до сервиса C. Изменение в требуемом порядке вызовов с большой вероятностью отразится на всех сервисах выше и ниже в цепочке, потому что их реализация должна измениться в соответствии с новыми требованиями к порядку.
Но рис. 2.3 не раскрывает полной картины. Сервис B на рис. 2.3 кардинально отличается от сервиса B на рис. 2.1. Исходный сервис B выполнял только функциональность B. Сервис B на рис. 2.3 должен знать о существовании сервиса C, а в контракт B должны быть включены параметры, которые потребуются сервису C для выполнения его функциональности. На рис. 2.1 за эти подробности отвечал клиент.
Проблема усугубляется сервисом A. Теперь он должен включить в свой контракт параметры, необходимые для вызова сервисов B и C, чтобы те могли выполнить свою соответствующую бизнес-функциональность. Любые изменения в функциональности B и C отражаются в изменениях реализации сервиса A, который теперь тесно связан с ними. Разбухание и привязки такого рода представлены на рис. 2.4.
Рис. 2.4. Объединение функциональности в цепочки приводит к разбуханию сервисов
Как ни печально, даже рис. 2.4 не показывает всей правды. Допустим, сервис A успешно выполнил функциональность A, после чего перешел к вызову сервиса B для выполнения функциональности B. Однако сервис B столкнулся с ошибкой и не смог нормально отработать. Если сервис A вызывал B синхронно, то он должен располагать полной информацией о внутренней логике и состоянии B, чтобы провести восстановление после ошибки. А это означает, что функциональность B также должна присутствовать в сервисе A. Если A вызывает B асинхронно, то сервис B должен каким-то образом вернуть информацию A для отмены функциональности A или же выполнить откат A собственными силами. Другими словами, функциональность A также должна присутствовать в B. Тем самым создается тесная связь между сервисами B и A, а сервис B разбухает из-за необходимости компенсировать успешное выполнение сервиса A. Ситуация изображена на рис. 2.5.
Проблема усугубляется сервисом C. Что, если функциональности A и B отработали успешно и завершились, но сервис C не смог выполнить свою бизнес-функцию? Сервис C должен вернуться к сервисам B и A для отмены их операций. Это приводит к дополнительному разбуханию сервиса C и его связыванию с сервисами A и B. Если взять ситуацию на рис. 2.5, что потребуется для
Рис. 2.5. Дополнительное разбухание и связи из-за необходимости компенсации
замены сервиса B сервисом B’, который выполняет свою функциональность не так, как B? Какие нежелательные последствия это будет иметь для сервисов A и C? И какая степень повторного использования достигается на рис. 2.5, если функциональность сервисов будет востребована в других контекстах — скажем, если сервис B вызывается после D, но до E? Что собой представляют A, B и C — три разнородных сервиса или одну спекшуюся бесформенную массу?
Относительно функциональной декомпозиции
В функциональной декомпозиции кроется почти неудержимый соблазн. Она кажется простым и логичным способом проектирования системы: от вас требуется всего лишь составить список необходимых функциональностей, а затем создать в архитектуре компонент для каждого пункта. Функциональная декомпозиция (и ее близкий родственник — декомпозиция предметных областей, о которой будет рассказано ниже) применяется при проектировании большинства систем. Многие проектировщики считают функциональную декомпозицию естественным выбором, и скорее всего, ваш преподаватель информатики в институте объяснял вам именно этот способ. Распространенность функциональной декомпозиции в плохо спроектированных системах — почти идеальный признак того, что от нее стоит держаться подальше. Всеми силами сопротивляйтесь искушениям функциональной декомпозиции.
О природе Вселенной (TANSTAAFL)
Чтобы доказать, что функциональная декомпозиция нежизнеспособна, можно вообще обойтись без аргументов из области программирования. Доказательство кроется в самой природе Вселенной, а конкретно в первом законе термодинамики. Если отбросить математику, первый закон термодинамики просто гласит, что нельзя создать что-то полезное без приложения усилий. В просторечии это называется принципом TANSTAAFL (сокращение от «There ain’t no such thing as a free lunch», то есть «бесплатных завтраков не бывает» — в смысле «даром ничего не дается»).
Проектирование по своей природе является деятельностью с высокой добавочной ценностью. Вы читаете эту книгу вместо еще одной книги по программированию именно потому, что вы понимаете полезность проектирования, или, говоря иначе, вы считаете, что проектирование обладает добавочной ценностью (и немалой!).
Проблема функциональной декомпозиции заключается в том, что она пытается обойти первый закон термодинамики. Результат функциональной декомпозиции (а именно проектирование системы) должен быть деятельностью с высокой добавочной ценностью. Однако функциональная декомпозиция проста и прямолинейна: для заданного набора требований, предусматривающих выполнение функциональностей A, B и C, вы проводите декомпозицию на сервисы A, B и C. «Проще простого! — говорите вы. — Функциональная декомпозиция настолько проста, что с ней справится даже программа». Но именно потому, что этот способ проектирования отличается быстротой, простотой, механистичностью и прямолинейностью, он противоречит первому закону термодинамики. Так как ценность не может добавляться без усилий, сами атрибуты, которые делают функциональную декомпозицию столь привлекательной, не позволяют ей создавать добавочную ценность.
Антипроектирование
Убедить коллег и руководство использовать что-то другое вместо функциональной декомпозиции — весьма непростое дело. «Мы всегда так делали», — скажут они вам. На этот довод можно привести два возражения. Во-первых, можно ответить: «И сколько раз мы выдерживали сроки или бюджет, которые были утверждены изначально? Что у нас получалось с качеством и сложностью? Насколько просто осуществлялось сопровождение системы?»
Во-вторых, можно провести сеанс антипроектирования. Сообщите команде, что вы проводите конкурс по проектированию системы следующего поколения. Разбейте команду на две половины и рассадите их в разных комнатах. Предложите первой половине разработать лучшее проектировочное решение для системы. Затем предложите второй половине разработать худшее из всех возможных решений: то, которое бы предельно затрудняло сопровождение и расширение системы, которое бы препятствовало повторному использованию и т.д. Дайте им поработать в течение нескольких часов, а затем соберите вместе. При сравнении результатов обычно выясняется, что они выдали практически одинаковые результаты. Подписи на компонентах могут различаться, но суть останется неизменной. Только теперь признайтесь, что они работали над разными задачами, и обсудите последствия. Возможно, пришло время поискать новый подход.
Пример: функциональный дом
Тот факт, что при проектировании никогда не следует применять функциональную декомпозицию, — универсальное наблюдение, которое не имеет никакого отношения к программным системам. Возьмем построение функциональности дома, как если бы он был программной системой. Все начинается с перечисления необходимой функциональности: приготовление еды, игры, отдых, сон и т.д. Затем для каждого вида функциональности в архитектуре создается отдельный компонент, как показано на рис. 2.6.
Рис. 2.6. Функциональная декомпозиция дома
Хотя рис. 2.6 уже выглядит нелепо, настоящее безумие проявляется только тогда, когда приходит время строить дом. Вы начинаете с пустого участка земли и беретесь за приготовление еды. Только приготовление еды. Вы вынимаете микроволновку из коробки и откладываете ее в сторону. Затем вы заливаете бетоном маленькую площадку, ставите на нее деревянный каркас, накрываете крышкой и ставите на нее микроволновку. Остается построить для микроволновки маленький чулан, сколотить над ней крошечную крышу и подключить к электросети. «Порядок, теперь можно готовить!» — объявляете вы начальству и заказчикам.
Но так ли это? Разве можно готовить еду таким способом? Где вы собираетесь ее подавать, где хранить остатки, куда выкидывать мусор? А как насчет приготовления еды на газовой плите? Что потребуется для того, чтобы повторить эту процедуру для плиты? Какая степень повторного использования может быть достигнута между этими двумя разными способами выражения функциональности приготовления еды? Можно ли легко расширить только одну из них? А если вам потребуется переставить микроволновку в другое место? Все эти проблемы даже не могут считаться первым шагом, потому что все зависит от того, что именно вы готовите. Возможно, вам придется строить разную функциональность приготовления еды, если в процессе приготовления задействованы разные кухонные устройства, а результаты могут отличаться по контексту — например, если вы готовите завтрак, обед, ужин, десерт или легкую закуску. В результате вы получаете либо мириады крошечных сервисов, предназначенных для конкретного сценария, который должен быть известен заранее, либо получается один огромный сервис, в котором есть все. Возможно ли построить дом при подобном подходе? А если нет, то стоит ли так проектировать и строить программные системы?
Когда применяется функциональная декомпозиция
Все это глумление вовсе не означает, что функциональная декомпозиция в принципе плоха. У функциональной декомпозиции есть свое место — это превосходный метод выявления требований. Она помогает архитекторам (или руководителям продукта) выявлять скрытые или неочевидные области функциональности. Даже при нечетких функциональных требованиях можно начать с верхнего уровня и провести функциональную декомпозицию до уровня с высокой детализацией. Вы выявляете требования и отношения между ними, встраиваете требования в древовидную систему и выявляете избыточные аспекты или взаимоисключающую функциональность. Тем не менее попытка превратить функциональную декомпозицию в проектировочное решение ведет к фатальным последствиям. Между требованиями и проектировочным решением никогда не должно быть прямого соответствия.
Недостатки декомпозиции предметной области
Проектное решение дома на рис. 2.6 очевидно абсурдно. Скорее всего, в своем доме вы предпочитаете готовить на кухне, поэтому на рис. 2.7 изображена альтернативная декомпозиция дома. Такая форма декомпозиции называется декомпозицией предметной области: система разбивается на структурные элементы в соответствии с предметными бизнес-областями: продажи, техническое обеспечение, бухгалтерия, отгрузка и т.д. К сожалению, декомпозиция предметной области (рис. 2.7) на практике работает еще хуже функциональной декомпозиции на рис. 2.6. Причина заключается в том, что она остается замаскированной функциональной декомпозицией: на кухне (Kitchen) вы готовите, в спальне (Bedroom) спите, в гараже (Garage) ставите машину и т.д.
Собственно, каждая из функциональных областей на рис. 2.6 может быть отображена на предметные области на рис. 2.7, что создает серьезные проблемы. Каждая спальня может быть уникальной, но функциональность сна должна дублироваться во всех спальнях. Дальнейшее дублирование возникает тогда, когда вы спите перед телевизором в гостиной или развлекаете гостей на кухне (практически все вечеринки почему-то заканчиваются на кухне).
Рис. 2.7. Декомпозиция предметной области для дома
Каждая предметная область часто деградирует до уродливой свалки функциональности, что только повышает ее внутреннюю сложность. Повышение внутренней сложности заставляет разработчика держаться подальше от проблем межобластных связей, а взаимодействие между предметными областями обычно сокращается до простых изменений состояния (в стиле CRUD) вместо действий, инициирующих выполнение нужного поведения во всех областях. Компоновка более сложных вариантов поведения между предметными областями становится очень сложной задачей. Некоторые функциональности просто не могут быть представлены в декомпозициях предметных областей. Для примера возьмем дом на рис. 2.7: где бы вы готовили еду, которая не может готовиться на кухне (например, барбекю)?
Построение дома с декомпозицией предметной области
Как и с чисто функциональным подходом, настоящие проблемы с декомпозицией предметной области становятся очевидными в ходе построения. Представьте, что вы строите дом на основе декомпозиции на рис. 2.7. Строительство начинается с пустого участка земли. Вы роете котлован для фундамента кухни, заливаете его бетоном (только для кухни!) и добавляете арматуру. Затем вы воздвигаете стены кухни (все они должны быть внешними), закрепляете их на фундаменте, прокладываете электропроводку и трубы в стенах; подключаете кухонное оборудование к водопроводу, газопроводу и электросети; устанавливаете системы обогрева и охлаждения; ставите счетчики расхода воды, электричества и газа; строите крышу над кухней; красите стены изнутри; развешиваете шкафы; покрываете штукатуркой наружные стены (то есть все стены) и красите их. Наконец, вы объявляете заказчику, что модуль Kitchen готов, а контрольная точка 1.0 достигнута.
Затем вы переходите к спальне. Сначала вы сбиваете штукатурку с кухонных стен, чтобы обнажить крепления стен, и отсоединяете кухню от фундамента. Вы отключаете кухню от газопровода, водопровода и электросети и канализации, а затем при помощи дорогостоящих гидравлических домкратов поднимаете ее. Пока кухня висит в воздухе, вы сдвигаете ее в сторону, чтобы вам было удобнее разломать фундамент отбойными молотками, выкинуть мусор и оплатить ее вывоз. Теперь можно вырыть новый котлован под общий фундамент для спальни и кухни. Вы заливаете котлован бетоном и устанавливаете новые крепления, стараясь разместить их точно в тех местах, что и прежде. Затем вы очень осторожно опускаете кухню на новый фундамент и убеждаетесь в том, что все крепления и отверстия совпадают (что почти невозможно). Вы строите новые стены для спальни. Потом вы временно снимаете шкафы с кухонных стен, сбиваете штукатурку, чтобы получить доступ к вмонтированным проводам и трубам, и соединяете все вентиляционные каналы, трубы и провода с каналами, трубами и проводами в спальне. Вы снова покрываете штукатуркой стены в кухне и спальне, вешаете обратно шкафы и расставляете мебель в спальне. Всю оставшуюся на стенах кухни штукатурку приходится сбить, чтобы наложить сплошную штукатурку без трещин на внешние стены. Некоторые стены кухни, которые прежде были внешними, теперь стали внутренними — со всеми последствиями для штукатурки, изоляции, краски и т.д. Остается снять крышу с кухни и возвести новую общую крышу для кухни и спальни. Теперь можно сообщить заказчику, что контрольная точка 2.0 достигнута, а модуль Bedroom1 готов.
О том факте, что вам пришлось перестраивать кухню, никто вслух не говорит — как и о том, что повторное строительство кухни обошлось намного дороже и было сопряжено с большим риском, чем первое. А если вам потребуется добавить к этому дому еще одну спальню? Сколько раз вы будете строить и ломать кухню? Сколько раз вообще можно перестроить кухню, прежде чем она рассыплется в передвигаемую с места на место груду бесполезного мусора? Была ли кухня действительно готова в тот момент, когда вы об этом объявили? Даже если забыть о потерях на перестройку, в какой степени возможно повторное использование различных частей дома? Насколько дороже обойдется строительство при таком подходе? И стоит ли применять такой подход для построения программной системы?
Ошибочная мотивация
Стандартный довод для применения функциональной декомпозиции или декомпозиции предметной области — заказчик хочет получить ту или иную функцию как можно быстрее. Проблема в том, что одна функция никогда не может быть реализована в изоляции от других. Подсистема оплаты не обладает бизнес-ценностью независимо от подсистем выставления счетов и отгрузки.
С унаследованными системами ситуация становится еще хуже. Разработчикам редко достается такая роскошь, как построение совершенно новой системы на пустом месте. Гораздо вероятнее, что им достанется существующая разваливающаяся система, которая была спроектирована на основе функциональной декомпозиции; негибкость и высокие затраты на сопровождение оправдывают разработку новой системы.
Предположим, ваш бизнес использует три функциональности, A, B и C, работающие в унаследованной системе. Когда вы строите новую систему для замены старой, вы решаете построить — и что еще важнее, развернуть — функциональность A в первую очередь, чтобы удовлетворить заказчиков и руководителей, которые желают видеть создаваемую ценность как можно раньше и чаще. Проблема в том, что бизнесу не нужна функциональность A сама по себе — только в сочетании с B и C. Если A выполняется в новой системе, а B и C — в старой, решение работать не будет, потому что старая система не знает о новой и не может выполнять только B и C. Выполнение A как в старой, так и в новой системе не создает никакой ценности и даже сокращает ценность из-за дублирования работы, так что, скорее всего, пользователям это не понравится. Единственный выход — как-то согласовать новую систему со старой. Обычно такое согласование по сложности намного превосходит исходную бизнес-задачу, так что в итоге разработчикам придется решать намного более сложную задачу. Возвращаясь к аналогии с домом, что бы вы сказали, если бы вам пришлось жить в тесном старом доме, пока на другой стороне города строится новый дом по плану на рис. 2.6 или 2.7? Допустим, вы строите только подсистему приготовления еды или кухню в новом доме, но при этом продолжаете жить в старом. Каждый раз, когда вы проголодаетесь, вам придется ехать в новый дом и возвращаться обратно. Со своим домом вы бы на такое не согласились, и навязывать подобные неудобства заказчикам не следует.
Тестируемость и проектирование
Критический недостаток как функциональной декомпозиции, так и декомпозиции предметной области связан с тестированием. В таких архитектурах количество связей и сложность настолько высоки, что единственный вид тестирования, на который могут пойти разработчики, — это модульное тестирование. Впрочем, это не показатель важности модульного тестирования, а всего лишь еще один пример «эффекта уличного фонаря»6 (когда вы ищете не там, где потеряли, а где светлее и где легче найти).
Физические и программные системы
В этой книге я использую примеры из материального мира (например, дома) для демонстрации общих принципов проектирования. В отрасли разработки часто встречается мнение, что принципы проектирования физических сущностей нельзя экстраполировать на программы, что процессы проектирования и построения программ каким-то образом избавлены от ограничений на проектирование или процессы физических систем. В конце концов, в программе можно сначала нарисовать дом, а затем построить стены «под краску». Вам не нужно возиться с такими подробностями, как балки и кирпичи.
Я обнаружил, что отрасль не только может заимствовать опыт и передовые приемы из материального мира, но и обязана это делать. Вопреки интуитивным представлениям, для программ проектирование еще более необходимо, чем для физических систем. Причина в сложности. Сложность физических систем, таких как типичный дом, должна учитывать физические ограничения. Невозможно построить плохо спроектированный дом с сотнями взаимосвязанных коридоров и комнат. Стены окажутся слишком тяжелыми, будут содержать слишком много проемов, окажутся слишком тонкими, двери будут слишком маленькими или проект окажется слишком дорогостоящим для реализации. Невозможно использовать слишком много строительных материалов, потому что дом обрушится, вам не хватит средств на их покупку или места для хранения запасов.
Без таких естественных физических ограничений сложность программных систем может быстро выйти из-под контроля. Обуздать эту сложность можно только одним способом: применением хороших инженерных методов, для которых проектирование и технологические процессы играют основополагающую роль. Хорошо спроектированные программные системы очень похожи на физические сущности и строятся практически по тем же правилам. Они напоминают хорошо спроектированные механизмы.
Функциональная декомпозиция или декомпозиция предметной области не имеет смысла при проектировании и построении домов или программных систем. Все сложные сущности (физические или нет) обладают одинаковыми абстрактными атрибутами, от дерева решений проектирования до критического пути выполнения. Все составные системы должны проектироваться так, чтобы они были безопасными, простыми в сопровождении, пригодными для повторного использования, расширяемыми и высококачественными. Это утверждение относится как к домам, так и к частям машин или программным системам. Все это непременные атрибуты практической инженерной деятельности, и реализовать и поддерживать их можно только одним способом: применением универсальных инженерных практик.
Несмотря на все сказанное, между физическими и программными системами существует принципиальное различие: видимость. Любой, кто попытается построить дом на рис. 2.6 или 2.7, будет немедленно уволен. Такой человек явно неадекватен, а ужасающие потери строительных материалов, времени и средств, а также высокий риск травматизма будут очевидны всем окружающим. С программными системами дело обстоит иначе: колоссальные потери тоже присутствуют, но они скрыты. В программировании пыль и строительный мусор заменяются загубленными карьерными перспективами, энергией и молодостью. Тем не менее никто никогда не видел и не обращал внимания на эти скрытые потери, а безумие не только разрешается, но и поощряется, словно психушка перешла под контроль пациентов. Правильное проектирование позволит вам освободиться и вернуть контроль над происходящим за счет исключения этих скрытых потерь. Как будет показано в части II книги, к планированию проектов это относится еще в большей мере.
Как ни печально, модульное тестирование находится на грани бесполезности. Хотя модульное тестирование является неотъемлемой частью тестирования, оно не способно протестировать систему. Представьте авиалайнер с множеством внутренних компонентов (насосы, приводы, сервомеханизмы, турбины и т. д). Теперь предположим, что все компоненты по отдельности идеально прошли модульное тестирование, но все тестирование выполнялось только до сборки самолета из компонентов. Рискнули бы вы сесть в такой самолет? Причина, по которой модульное тестирование имеет крайне ограниченную ценность, состоит в том, что в любой сложной системе дефекты не скрываются в модулях, а возникают в результате взаимодействия между модулями. Вот почему вы инстинктивно понимаете, что каждый компонент лайнера может отлично работать, но результат их сборки может быть абсолютно ошибочным. Что еще хуже, даже если сложная система пребывает в идеальном состоянии с безупречным качеством, изменение одного компонента, прошедшего модульное тестирование, может нарушить работу другого модуля (или модулей), зависящего от старого поведения. При изменении всего одного модуля приходится повторять тестирование всех модулей. Но даже тогда это будет бессмысленно, потому что изменение одного из компонентов может повлиять на взаимодействия между другими компонентами или подсистемами, которые не сможет выявить никакое модульное тестирование.
Единственный способ проверки изменений — полное регрессионное тестирование системы, ее подсистем, ее компонентов и взаимодействий и, наконец, ее модулей. Если в результате изменения придется вносить изменения в другие модули, то его эффект для регрессионного тестирования будет нелинейным. Идея о неэффективности модульного тестирования далеко не нова, она была продемонстрирована на тысячах систем с вычислением объективных метрик.
В теории регрессионное тестирование можно применять даже в системе с функциональной декомпозицией. На практике сложность этой задачи устанавливает очень высокую планку. Из-за гигантского количества функциональных компонентов тестирование всех взаимодействий становится практически нецелесообразным. Внутреннее устройство очень больших сервисов окажется настолько сложным, что никто не сможет эффективно разработать полноценную стратегию для тестирования всех ветвей выполнения таких сервисов. С функциональной декомпозицией многие разработчики обычно сдаются и ограничиваются простым модульным тестированием. Таким образом, функциональная декомпозиция, исключающая возможность регрессионного тестирования, делает всю систему нетестируемой, а нетестируемые системы всегда изобилуют дефектами.
Пример: функциональная торговая система
Вместо дома рассмотрим следующие упрощенные требования к системе биржевой торговли для финансовой компании:
• Система должна предоставлять внутренним брокерам следующие возможности:
• покупка и продажа акций;
• планирование сделок;
• построение отчетов;
• анализ результатов.
• Пользователи системы используют браузер для подключения к системе и управления сеансами, заполнения форм и отправки запросов.
• После запроса на проведение сделки, построение отчета или проведение анализа система отправляет пользователям сообщения электронной почты с подтверждением или результатами запроса.
• Данные должны храниться в локальной базе данных.
Прямолинейная функциональная декомпозиция дает результат, показанный на рис. 2.8.
Каждое из функциональных требований выражается соответствующим компонентом архитектуры. На рис. 2.8 представлено обобщенное проектировочное решение, за которое многие начинающие разработчики возьмутся без малейших сомнений.
Рис. 2.8. Функциональная торговая система
Проблемы функциональной торговой системы
Такое проектировочное решение имеет множество недостатков. Весьма вероятно, что клиент в представленной системе будет координировать покупку акций, продажу акций и планирование операций; выдавать запросы на построение отчетов и т.д. Предположим, пользователь хочет профинансировать покупку определенного количества акций за счет продажи других акций. Это подразумевает два запроса: сначала на продажу, затем на покупку. Но что делать клиенту, если к моменту выполнения этих двух транзакций цена продаваемых акций упала или цена покупаемых акций поднялась так, что продажа не сможет покрыть покупку? Должен ли клиент купить столько акций, сколько сможет? Или продать больше акций, чем предполагалось изначально? Может ли он заимствовать средства из расходного счета, связанного с учетной записью продавца, для финансирования заказа? А может, отменить всю сделку? Или обратиться за помощью к пользователю? Точная процедура разрешения проблемы несущественна для этого обсуждения. Какой бы способ ни был выбран, он потребует бизнес-логики, которая теперь находится в клиенте.
А что потребуется для того, чтобы клиент смог работать с системой не через веб-портал, а из приложения на мобильном устройстве? Не приведет ли это к дублированию бизнес-логики на мобильном устройстве? Скорее всего, бизнес-логику и усилия, затраченные на разработку веб-клиента, практически не удастся повторно использовать в мобильном клиенте, потому что они встроены в веб-портал. Со временем все придет к тому, что разработчики начнут поддерживать несколько версий бизнес-логики в разных клиентах.
Что касается требований, подсистемы Покупка акций, Продажа акций, Планирование операций, Построение отчетов и Анализ уведомляют пользователя о своей деятельности по электронной почте. А если пользователь предпочитает получать текстовые сообщения (или бумажные письма) вместо электронной почты? Придется изменять реализацию подсистем Покупка акций, Продажа акций, Планирование операций, Построение отчетов и Анализ и переделывать электронную почту в текстовые сообщения.
В соответствии с проектировочным решением информация хранится в базе данных, а подсистемы Покупка акций, Продажа акций, Планирование операций, Построение отчетов и Анализ обращаются к базе данных. Теперь предположим, что вы решите переместить данные из локальной базы данных в облачное решение. Как минимум это заставит вас изменить код обращения к данным в подсистемах Покупка акций, Продажа акций, Планирование операций, Построение отчетов и Анализ. Структура, процессы обращения к данным и их потребления должны быть изменены во всех компонентах.
Что, если клиент захочет взаимодействовать с системой асинхронно — выдать несколько команд на выполнение операций и получить результаты позднее? Компоненты были построены в предположении о подключенном синхронном клиенте, который координирует работу компонентов. Ск
