“Где просто — там ангелов со сто, а где мудрено — там ни одного”.
Из словаря народных пословиц и поговорок В. И. Даля
Готовясь к вечеринке, я захожу в винный магазин, чтобы взять хорошего сухого итальянского вина. Я знаком с французским и испанским вином, а вот итальянское для меня в новинку, но вино именно из этой страны будет пропуском на веселое мероприятие в этот раз. Я знаю приемлемую для меня ценовую категорию, но как сделать правильный выбор в представленном на магазинных полках разнообразии марок? Конечно, если бы я уже имел опыт дегустации итальянских вин, мне было бы легче: удачный опыт делает выбор очень простым, а неудачный — сокращает возможные варианты. Такого опыта у меня, к сожалению, нет, и я начинаю рассматривать красивые этикетки в надежде найти какую-нибудь подсказку. Вообще для меня основным критерием покупки неизвестных алкогольных напитков всегда было количество бутылок, оставшихся на полке. Чем меньше бутылок определенной марки, тем оно более популярно (если только эти бутылки не были специально убраны с прилавка), а следовательно, тем больше вероятность его хорошего качества. Но сейчас это не работает: число бутылок разных марок вина почти одинаково. Стою и жду — может, кто-нибудь более искушенный, чем я, возьмет вино с моей полки, и я скопирую этот выбор, надеясь на его компетентность. Нет, похоже, все-таки придется обратиться с вопросом к работнику магазина. Но проблема в том, что у них качество вина почти всегда определяется его ценой. Продавец обязательно укажет на самое дорогое, и не факт, что оно действительно будет хорошим, а в результате может пострадать моя, пусть и не совсем справедливая, репутация винного знатока. Время идет, а я все никак не могу решиться. А может, просто закрыть глаза, протянуть руку и выхватить первую попавшуюся бутылку? Я отрываю свой взгляд от этикеток и — о чудо! — невдалеке вижу дегустационный столик с пробами как раз тех вин, которые меня интересуют. Вот теперь я точно найду решение возникшей у меня сложной проблемы…
Сделать выбор очень просто: находишь критерий выбора, выстраиваешь согласно ему все альтернативы в ряд, и вот оно — правильное решение. Но как определить правильный критерий выбора? Является ли он в каждом случае объективной величиной, или все зависит от конкретного человека? А что, если параметров сравнения больше, чем один, — как тогда не пойти по ложному пути? К сожалению, для общего случая точных ответов на эти вопросы нет. Только для конкретной задачи с ее известными условиями можно сделать какие-либо предположения о том, в каком направлении следует двигаться, натолкнувшись на развилку дорог, именуемую выбором.
При разработке программного обеспечения проблема выбора является постоянным источником головной боли, поскольку нет таких задач для ПО, за исключением, конечно, самых примитивных, которые можно было бы решить только одним способом. Эта проблема преследует разработчика начиная с момента формирования требований к проекту и заканчивая процессами кодирования и тестирования. Часто предпочтение той или иной альтернативы является достаточно очевидным, но бывает, что поиск приемлемого варианта требует многих часов раздумья. Почему так важно сделать правильный выбор при решении возникающих задач в программном проекте? Прежде всего потому, что неверное решение стоит дорого. Оно приводит к дополнительным ресурсным затратам, в первую очередь людским и временным, которые в конечном счете выливаются в удорожание всего проекта. При разработке ПО чем раньше делается неправильный выбор, тем серьезнее могут быть его последствия. Например, при ошибочном выборе архитектуры может быть поставлен под сомнение сам проект.
Как же найти критерий выбора, который сможет претендовать на роль основного при разработке ПО? Для того чтобы попытаться ответить на этот вопрос, нужно выяснить, что же для ПО является наибольшей проблемой.
Когда программный продукт содержит скудный функциональный набор, решает только небольшой и очень ограниченный круг задач, выделить и обобщить какие-либо проблемы бывает трудно. При расширении функциональности и добавлении новых возможностей начинает проявляться порок, который на сегодняшний день является основной и наиболее значимой проблемой для ПО. Именно из-за этой проблемы возникают трудности в его применении. Именно она является источником неадекватного поведения ПО, его нестабильной и ненадежной работы. Поэтому на борьбу с нею направлены основные усилия проектировщиков и программистов. Имя этой проблемы — сложность. Сложность — это универсальное понятие зла в программном обеспечении. В нем концентрируется все то плохое, что мешает ПО быть идеальным многофункциональным инструментом. Сложность программного обеспечения напрямую зависит от количества входящих в него компонентов, степени их взаимной связи и интенсивности взаимодействия. У ПО можно различить внешнюю и внутреннюю сложность, которые могут быть как связаны друг с другом, так и нет.
Внешней стороной ПО являются его пользовательский и программный интерфейсы. Запутанный интерфейс со множеством условий, трудно улавливаемая связь между отдельными функциями, обилие инструментальных панелей с многочисленными кнопками, несогласованная работа отдельных частей, опускание на самую глубину интерфейса действительно важных функций и выпячивание наружу малозначимых операций — вот далеко не полный перечень внешних проблем ПО, из за которых мы чувствуем на себе тяжелое дыхание демона сложности. Можно с уверенностью сказать, что задачей внешней стороны ПО в лице пользовательского и программного интерфейсов должна быть его максимально возможная простота. Если новому программному продукту ставится диагноз “сложный”, то можно быть уверенным, что век его будет недолгим. Поскольку стоит только выйти конкурирующему продукту, который выполняет те же функции, но более простым способом, как старые программы немедленно исчезнут с нашего десктопа. Вообще умение делать что-то проще, чем все остальные, является прямой дорогой к успеху. За примерами далеко ходить не надо: кто до эпохи Google думал, что поиск может быть таким простым и изящным и в то же время мощным и эффективным? То же в полной мере справедливо и по отношению к программным технологиям и методикам. Только те технологии, которые базируются на простых идеях и реализуют их максимально простыми способами, остаются на плаву.
Исходя из своего опыта разработки ПО для авиационной индустрии, могу сказать, что основным препятствием использования новых программных технологий в этой области является их сложность. Например, трехмерная графика пока не может прижиться на бортовых компьютерах самолета. И дело даже не в том, что для построения такой графики нужны высокоточные данные и дополнительные компьютерные ресурсы. Само понимание трехмерной информации часто вызывает трудности у пилотов. Поэтому, например, электронные полетные и навигационные карты представлены на борту только в 2D-формате. Что же касается 3D-технологий, то они для бортовых компьютеров пока находятся только на самом начальном этапе развития и имеют еще много проблем.
Внутренняя сложность является одной из самых значительных проблем в области ПО. Каждый новый кусок кода, добавленный в программную систему, повышает ее сложность. Для того чтобы общая сложность всей системы не перешагнула ту грань, за которой начинается нестабильная работа ПО, должны быть применены методы, позволяющие управлять этой сложностью. Если посмотреть на всю историю развития технологии и методологии программирования, можно с уверенностью сказать, что все основные усилия были направлены на решение именно этой проблемы. Каждая новая выходящая методология предлагала свой способ уменьшения сложности и методы ее контроля, тем самым позволяя строить все более комплексные и многофункциональные программные системы. Чем лучше методология решала эту задачу, тем больше сторонников она приобретала. Огромная популярность объектно-ориентированного подхода в программировании (ООП) связана с его большой эффективностью в решении проблемы сложности. Нахождение оптимального сочетания всех доступных средств, приводящих к максимально возможному уменьшению сложности программной системы без потери его функциональности, — так можно охарактеризовать основную задачу разработчика ПО.
Теперь вернемся к проблеме выбора. Согласно нашим рассуждениям при разработке ПО главным критерием выбора решения возникающих задач является поиск такой альтернативы, которая приведет к минимальному увеличению сложности всей программной системы. Отсюда следует, что для решения конкретной задачи нужно реализовать все возможные альтернативы и по критерию сложности выбрать ту, которая и должна быть использована в проекте. Для программного кода часто бывает проще проверить все предполагаемые решения сначала отдельно от проекта, а затем наиболее предпочтительные протестировать вместе со всеми модулями системы для выявления оптимального.
Идея полного перебора всех возможных вариантов кажется хорошей до тех пор, пока мы не сталкиваемся с практикой. Дело в том, что в реальной жизни при разработке ПО начинает действовать другой фактор — субъективный, а именно субъективная сложность. Эта сложность зависит от конкретного человека и выражается в том, насколько проблематично именно для него реализовать то или иное решение.
При рассмотрении каждого варианта наша оценка сложности складывается из предполагаемого времени, которое будет потрачено на его изучение, плюс время на приобретение необходимых навыков его использования и плюс затраты на его реализацию. Оценка эта исключительно субъективная и зависит от наших знаний и опыта в той области, где мы работаем. Чем ближе решение к тому, что мы уже делали, тем меньше времени мы на него затратим и тем больше у него шанс быть нами избранным. И наоборот, чем более далек от нас предполагаемый вариант, тем больше у него вероятность оказаться аутсайдером. Если у нас уже был положительный опыт решения подобной проблемы, мы без сомнений им воспользуемся, поскольку для нас такое решение имеет наименьшую сложность. При выборе альтернативы на наше предпочтение может повлиять усиленная реклама некоторых методов в тех источниках информации, которым мы доверяем, а также советы работающих с нами коллег. Даже имея на руках полное решение проблемы, мы можем его отвергнуть только потому, например, что нам сложно разобраться, как его использовать. Вот почему в некоторых проектах иногда можно найти решение одной и той же проблемы разными способами — они были реализованы различными разработчиками, которые посчитали, что изучать чей-то подход к задаче будет слишком накладно и проще реализовать свое собственное решение. Среди возможных альтернатив иной раз можно встретить комплексное решение, которое помогает справиться не только с нашей текущей проблемой, но и с рядом других, с ней связанных. Но ввиду высоких трудозатрат, неочевидности сегодняшней выгоды и туманности перспективы у него мало шансов быть выбранным.
Как правило, с опорой на подобную логику лидер нашего выбора находится легко. Если все же выявить явного фаворита сразу не удается, тогда мы начинаем полный перебор всех возможных вариантов, но только в том случае, если у нас есть на это достаточно времени. Если времени нет, то нередко мы просто берем первое попавшееся решение, которое нам кажется более-менее приемлемым. Но даже если мы решаемся на перебор всех альтернатив, нет гарантии того, что мы выберем самый оптимальный вариант, поскольку часто находимся в плену наших догадок, иллюзий и предпочтений и видим то, что нам хочется видеть.
Таким образом, разработчик при решении проблемы выбора исходит из своего субъективного понятия сложности. Поскольку это понятие не всегда совпадает с объективной сложностью, задача выбора решается не самим эффективным способом. Иногда понимание того, что выбор сделан неправильно, приходит только после окончания всех действий, связанных с его реализацией. И тогда возникает более суровый выбор — либо переделать все заново, либо оставить все как есть. Согласно принципу уменьшения объективной сложности надо все переделать, но нулевая субъективная сложность тянет нас совсем в другую сторону. Иногда в процессе реализации можно понять, что решение выбрано неправильно. Например, при создании кода мы можем заметить, что он затрагивает слишком много объектов, логически с ним не связанных, и при этом необходимо постоянно учитывать большое количество разнообразных деталей, без которых наш код не будет нормально функционировать. И опять же совсем не факт, что мы прервемся и начнем решать задачу заново.
Получается, что чем меньше на нас влияет субъективный фактор при выборе решения, тем выше вероятность выбора наиболее эффективного пути. А наименьшее влияние этот фактор оказывает на тех разработчиков, которые имеют высокую квалификацию в той предметной области, где они принимают решение. Отсюда следует довольно банальный вывод: чтобы правильно делать выбор, нужно стремиться иметь высокую квалификацию, а в быстроменяющейся области технологий разработки ПО это означает постоянное ее повышение. Обладая достаточными знаниями, навыками и опытом, мы становимся гораздо более точными в определении сложности того или иного пути решения и на наше мнение не могут повлиять случайные или второстепенные факторы.
Но даже наличие высокой квалификации не означает полного отказа во всех случаях от экспериментирования с различными вариантами решения проблемы. Квалификация лишь позволяет отбросить заведомо неправильные варианты, тем самым сужая поиск и уменьшая затраты на нахождение самого эффективного решения.
Иногда решение проблемы выбора напоминает мне мой поход в винный магазин: брать только известное вино, если я сильно спешу, выявлять косвенные признаки качества, если на это есть время, наблюдать за тем, кто что берет, если я не один в магазине, отыскивать самую красивую этикетку, если другие критерии не помогают, делать выбор с закрытыми глазами, если совсем ничего не выходит, и дегустировать, если мне нужно действительно хорошее вино.