От ремесла к науке: поиск основных принципов разработки ПО

Кони Бюрер, специалист в области разработки ПО, Rational Software

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

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

Ремесло в сравнении с инженерной наукой

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

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

Почему? Причина проста. Хотя средневековые архитекторы обладали глубокими познаниями о способах постройки высоких зданий, они ничего не знали о законах физики. Они знали, что форма арочного потолка должна быть конической, но они никогда не слыхали о дифференциальных уравнениях. Знание средневековых архитекторов было основано исключительно на опыте, полученном из проб и ошибок, и не имело никакого научного основания. Короче говоря, средневековые архитекторы знали как делать, но не знали почему надо делать именно так*. Проектирование и строительство зданий было ремеслом, а средневековые мастера архитектуры были ремесленниками, а не инженерами.

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

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

Что сегодня представляет собой разработка программного обеспечения?

Помня об этом отличии, зададим себе вопрос: является ли современная разработка ПО ремеслом, инженерной дисциплиной или чем-то средним? Многие разработчики ПО вероятно заявят, что программирование хотя и не сложилось в установившуюся инженерную дисциплину, но уже близко подошло к этому.

Я думаю, что это заблуждение. С моей точки зрения разработка ПО является чистым ремеслом. Она не может быть инженерной дисциплиной - и фактически не содержит никаких аспектов инженерной науки - потому, что она не имеет основных принципов. Все знание о способах разработки ПО основано исключительно на пробах и ошибках. Согласен, со времени зарождения программирования мы добились некоторых успехов. Такие мастера программирования, как, например, Грэйди Буч (Grady Booch), Джим Рамбау (Jim Rumbaugh) и Уокер Ройс (Walker Royce), изобрели в помощь разработчикам ПО успешные методы и правила, часто называемые оптимальными методиками . Но, подобно средневековым архитекторам, разработчики ПО изучили и испытали эти оптимальные методики путем проб и ошибок. Современным программистам не хватает системы основных принципов, на основе которых можно было бы строить свои правила и методы*.

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

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

В чем отличие разработки программного обеспечения?

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

Каждая инженерная дисциплина содержит элементы искусства и науки.

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

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

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

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

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

1. Если быть более точным, средневековый мастер не имел возможности формально обосновать свое знание. Например, если бы ученик спросил мастера: "Почему я должен делать это таким образом?", ответом было бы: "Потому, что я так сказал!" В противоположность этому, современный инженер ответил бы: "Согласно законам статики… если мы возьмем эту формулу…", и так далее, предлагая формальное обоснование необходимости поступить определенным образом.
2. Некоторые могут возразить, что оптимальные методики составляют основные принципы разработки ПО. К сожалению, это не так. Причина кроется в том, что мы не знаем, являются ли оптимальные методики действительно хорошими. У нас нет никакого понимания, почему методика разработки ПО заслуживает названия хорошей или плохой, кроме того, что она оказалась успешной в некоторых прошлых приложениях. Определение оптимальных методик путем проб и ошибок может со временем привести к более глубокому пониманию основных принципов и механизмов, определяющих качество методики разработки ПО. Но само по себе определение оптимальных методик не развивает программирование за пределы его текущей стадии ремесла.
3. Для ясности: основные принципы не являются руководящими принципами. Основные принципы являются четко определенными аксиомами, позволяющими формально проверить правила и процедуры программирования.
4. Позвольте конкретизировать эту мысль. Если спросить инженера, насколько надежен проект, он может ответить: "Я проверил его по формулам статики". Программист же ответил бы: "Я убежден, потому что он хорошо выглядит" - подобно художнику, оценивающему скульптуру.

Проектирование - внутренний характер разработки ПО

Проблемы разработки программного обеспечения? Есть простое пояснение!

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

Вопрос. Почему намного труднее создать структуру подробного перечня работ для создания программного обеспечения, чем для строительства небоскреба?

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

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

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

Вопрос. Почему масштабирование разработки ПО не влечет за собой соответствующего роста экономической эффективности?

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

Вопрос. Возможно ли повысить экономическую эффективность при масштабировании производства ПО?

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

Вопрос. Почему так низок уровень повторного использования программного обеспечения?

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

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

Для ответа на эти вопросы необходимо понять, что разработка программного обеспечения изначально является проектированием и не имеет признаков строительства или производства . Возможно, это утверждение трудно принять, но оно может быть легко обосновано. Мы хорошо понимаем, что такое проектирование, где оно заканчивается и где начинается строительство или производство. Рассмотрим два следующих аргумента:

  1. Граница между проектированием и строительством всегда четко обозначена артефактом - чертежом. Проектирование включает в себя все операции, необходимые для создания чертежа, а строительство охватывает все операции, необходимые для создания продуктов по этому чертежу. В идеальном случае чертеж должен определять создаваемый продукт во всех подробностях - что, конечно же, бывает очень редко. Тем не менее, целью чертежа является настолько подробное описание конструируемого продукта, насколько это возможно. Описывает ли проект архитектуры программной системы создаваемый продукт "во всех подробностях"? Нет. Проект архитектуры предназначен для описания существенных, но, безусловно, не всех подробностей программной системы. Поэтому очевидно, что проект архитектуры не является чертежом. Все подробности программной системы описываются только кодом на языке высокого уровня, который, таким образом, является чертежом программы. А поскольку все операции, ведущие к созданию чертежа, являются проектированием, то и вся разработка ПО должна считаться проектированием.
  2. Объем работ (время, деньги, ресурсы), необходимый для создания коммерческого продукта, всегда может быть разделен на проектировочную и производственную составляющие. В чем разница? Объем работ проектирования является общим для всех копий продукта и должен быть затрачен только один раз. Объем работ с точки зрения производства должен затрачиваться при создании каждой копии продукта. Программный продукт обычно представляет собой двоичный исполняемый файл программы, поставляемой на компакт-диске. Ясно, что усилия по созданию исходного кода программы - включая проект архитектуры, подробный проект и код на языке высокого уровня - должны быть затрачены лишь однажды, независимо от количества выпущенных копий программного обеспечения. Следовательно, усилия по созданию исходного кода программы являются целиком проектировочными, а вся разработка программного обеспечения является проектированием.

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

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

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

Метод проб и ошибок завел нас достаточно далеко. Но рост сложности современных программных систем подводит нас к жесткому пределу. За пределами определенного уровня сложности создание качественных архитектур методом проб и ошибок становится невозможным.

Предложение: основные принципы разработки программного обеспечения

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

Рассмотрим, как существование основного принципа - например, закона гравитации - влияет на архитектуру системы. С абстрактной точки зрения основной принцип делает не что иное, как вводит не подлежащие обсуждению требования, которые должен учитывать архитектор. Я буду называть эти требования аксиоматическими . Например, для строительства простым аксиоматическим требованием может быть следующее: "Здание должно выдерживать силу гравитации". Отличие аксиоматических требований состоит в том, что они относятся к каждой системе, которая когда-либо была или будет построена. Аксиоматические требования являются, в сущности, настолько очевидными и общими, что они, как правило, подразумеваются. Но из аксиоматических требований установившиеся инженерные дисциплины выводят систему архитектурных правил, которым должны соответствовать все заслуживающие внимания проекты. Проекты, нарушающие эти правила, очевидно не согласуются с основными принципами и, возможно, являются ошибочными.

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

  1. Программное обеспечение должно получать входные данные с одного или нескольких внешних (аппаратных) интерфейсов.
  2. Программное обеспечение должно выдавать выходные данные на один или несколько внешних (аппаратных) интерфейсов.
  3. Программное обеспечение должно вести внутренние данные, используемые и обновляемые в каждом цикле выполнения.
  4. Программное обеспечение должно преобразовывать входные данные в выходные (возможно с использованием внутренних данных).
  5. Программное обеспечение должно выполнять преобразование данных как можно быстрее.

Звучит глупо, не так ли? Но, поверьте, очень немного программных систем обладают архитектурой, которая очевидно удовлетворяет вышеприведенным аксиоматическим требованиям. Многие программные системы могут неявно соответствовать этим требованиям, но это не очевидно! *

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

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

Следующий шаг: определение универсальной модели проектирования

В моей следующей статье для журнала The Rational Edge , планируемой на январский выпуск, я собираюсь обратить внимание на правила проектирования, связанные с основными принципами и их аксиоматическими требованиями. Эти правила представляют весьма полезную универсальную модель проектирования при разработке ПО. Универсальная модель проектирования является первым шагом к преобразованию разработки ПО в предсказуемую и повторяемую инженерную деятельность. Не забудьте заглянуть сюда снова!

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

Страница сайта http://www.interface.ru
Оригинал находится по адресу http://www.interface.ru/home.asp?artId=4820