Книга «Жемчужины разработки. Чему мы научились за 50 лет создания ПО»
Совершенное программное обеспечение невозможно создать без изучения накопленного опыта.
Опыт — главный учитель, но медленный и нередко болезненный. Но зачем же нам повторять ошибки? Книга «Жемчужины разработки» поможет совершенствоваться быстрее и избежать многих проблем, обучаясь на опыте других людей, которые уже поднялись по кривой обучения. Карл Вигерс сформулировал 60 кратких практических уроков, которые подойдут для любых проектов, независимо от роли, отрасли, технологии или методологии.
Идеи и конкретные рекомендации охватывают шесть важнейших элементов успеха: требования, дизайн, управление проектами, культуру и командную работу, качество и совершенствование процессов. Для каждого из направлений Вигерс предлагает «первые шаги», позволяющие осмыслить собственный опыт, уроки с основными идеями, реальными примерами и действенными решениями и «следующие шаги» для внедрения опыта в вашем проекте, команде или организации. Эти знания нельзя получить в университете!
Разрабатывайте продукты так, чтобы их легко было использовать правильно и трудно — неправильно
Недавно я опробовал некоторые онлайн-калькуляторы прогнозирования продолжительности жизни. Многие такие калькуляторы упрощены и запрашивают всего несколько входных данных, а затем выдают расплывчатый прогноз. Я был рад найти калькулятор, который запрашивал не менее 35 единиц информации обо мне лично, семейном положении, болезнях и образе жизни. На сайте имелись раскрывающиеся списки, позволяющие выбирать конкретные значения из множества вариантов. Однако калькулятор имел одну небольшую проблему с дизайном пользовательского интерфейса (рис. 3.5).
Введя все данные, я нажал кнопку Рассчитать, чтобы узнать, сколько мне еще осталось. Однако оказалось, что вместо этого я случайно нажал кнопку Сбросить. Как показано на рис. 3.5, эти две кнопки имеют одинаковый стиль, плохо различимы и фактически соприкасаются друг с другом, что редко можно увидеть в пользовательском интерфейсе. Мало того, подсказка для запуска расчета появляется под кнопкой Сбросить, а не рядом с кнопкой Рассчитать, поэтому я инстинктивно нажал кнопку над подсказкой с текстом «Рассчитать». Когда я случайно нажал кнопку Сбросить, все введенные мною данные тут же исчезли. Мне пришлось начать процесс заново, так как меня все еще волновало мое будущее.
Пользователю этого сайта слишком легко ошибиться. Такие проблемы с дизайном раздражают. Может быть, я единственный пользователь, который когда-либо случайно нажал кнопку Сбросить, и тогда это моя проблема, а не сайта. Однако даже неформальное тестирование удобства использования могло бы выявить риск нажать не ту кнопку по ошибке. Три простых изменения могли бы улучшить этот дизайн.
- Разместить кнопки Рассчитать и Сбросить подальше друг от друга и поместить подсказки рядом с соответствующими им кнопками, чтобы пользователь с большей вероятностью нажал нужную ему кнопку.
- По-разному оформить кнопки Рассчитать и Сбросить, например, более рискованную кнопку Сбросить можно сделать меньше и окрасить ее в красный цвет, а кнопку Рассчитать — крупнее и окрасить в зеленый.
- Защитить пользователя от случайности, попросив подтвердить выполнение разрушительного действия, такого как удаление всех введенных им данных, если вдруг он по ошибке нажмет не ту кнопку.
Пользователи ценят системы, которые им понятны, предотвращают или исправляют их ошибки и взаимодействуют с ними, услужливо предоставляя ясные подсказки.
Эти свойства характерны для эффективного пользовательского интерфейса. Они помогают пользователям выполнять необходимые действия при работе с сайтом или приложением. Помимо удобства использования, дизайнеры также должны учитывать возможность ошибок и стараться предотвращать или помогать исправлять их. У дизайнеров есть четыре возможных варианта обращения с потенциальными ошибками (Wiegers, 2021).
- Не дать пользователю возможности ошибиться.
- Затруднить пользователю возможность ошибиться.
- Упростить исправление допущенной ошибки.
- Просто позволить ошибкам случиться. (Не поступайте так!)
Не давайте пользователю возможности ошибиться
Предотвращение ошибок — предпочтительная стратегия. Если пользователь должен вводить данные в изначально пустое поле ввода, то такое поле предполагает возможность ввода произвольных данных, которые программа обязательно должна проверить. Раскрывающиеся списки (или другие элементы управления) с допустимыми вариантами позволяют ввести только допустимые значения. Не давайте возможности ввести недопустимые варианты. Я видел раскрывающиеся списки для выбора даты окончания действия кредитной карты, которые включали годы, предшествующие текущему, что логически бессмысленно. Точно так же я видел элементы управления, позволяющие вводить несуществующие даты, например 30 февраля. Разрешение ввода неверных данных приведет к ошибке, когда приложение или веб-страница попытается обработать информацию.
Затрудните пользователю возможность ошибиться
Если нельзя лишить пользователя возможности ошибиться, то хотя бы затрудните ее. В примере с калькулятором ожидаемой продолжительности жизни, упомянутом выше, я предложил три способа уменьшить вероятность нажать по ошибке не ту кнопку. Другая хорошая практика — добавить поясняющий текст в диалоговых окнах, чтобы исключить двусмысленность в понимании реакции системы на каждый выбор. Не заставляйте пользователя вводить одну и ту же информацию дважды, потому что это удваивает вероятность ошибиться и занимает вдвое больше времени. Например, если форма запрашивает два адреса — доставки и выставления счета, — то дайте пользователю возможность указать, что они совпадают, установив флажок.
Упростите исправление допущенной ошибки
Несмотря на все ваши усилия, ошибки (допущенные пользователем или системой во время работы) все равно будут возникать. Предусмотрите для пользователя простую возможность исправлять такие ошибки. Простота исправления — характеристика устойчивой программной системы, а устойчивость — атрибут качества, описывающий, насколько хорошо продукт справляется с неожиданными входными данными, событиями и условиями работы. Особенно полезны в этом отношении многоуровневая функция отмены/возврата и четкие, содержательные сообщения, помогающие пользователю исправить любые ошибки. Загадочные числовые коды ошибок HTTP, запросов к базе данных или сетевых сбоев могут помочь при технической диагностике, но бесполезны для обычного пользователя.
Просто позвольте ошибкам случиться
Наименее желательный вариант — просто позволить ошибкам случиться и заставить пользователя самому разбираться с последствиями. Предположим, пользователь просит запустить некую процедуру, имеющую определенные предварительные условия, которые должны быть выполнены. Программное обеспечение должно проверять такие предварительные условия и помогать пользователю выполнить их, если это необходимо, а не просто продолжать работать в надежде на лучшее. Более того, оно вообще не должно запускать запрошенную процедуру, если предварительные условия не выполнены. Система должна сама обнаруживать невыполненные предварительные условия и сообщать о них как можно раньше, чтобы не тратить время пользователя впустую. Пользователи ценят системы, которые им понятны, предотвращают или исправляют их ошибки и взаимодействуют с ними, услужливо предоставляя ясные подсказки.
Кстати, калькулятор ожидаемой продолжительности жизни, который я попробовал, показал, что я, вероятно, проживу еще несколько лет. Это была хорошая новость, даже притом что на его использование ушло в два раза больше моего времени, чем предполагалось, из-за далеко не идеального дизайна пользовательского интерфейса.
Невозможно оптимизировать все желаемые атрибуты качества
Следующее приложение, которое я хотел бы получить, не должно иметь ошибок: никаких ошибок 404 «Страница не найдена» и никаких экранов со справкой, не соответствующих форме, с которой я работаю. Приложение не должно использовать много памяти или замедлять мой компьютер и обязано освободить всю использованную память по завершении. Приложение должно быть полностью безопасным: никто не должен иметь возможности украсть мои данные или выдать себя за меня. Оно должно мгновенно реагировать на каждую мою команду и быть абсолютно надежным. Я не хочу видеть никаких сообщений «Внутренняя ошибка сервера» или «Приложение не отвечает». Пользовательский интерфейс никогда не должен позволять мне ошибаться. Приложение должно позволять работать с ним на любом устройстве, мгновенно загружаться, не прерывать работу по тайм-ауту, импортировать и экспортировать любые данные из других источников. Ах да, чуть не забыл — приложение должно быть бесплатным.
Похоже на сказочное приложение? Верно! Мои желания разумны? Конечно, нет!
Невозможно объединить в одном приложении все лучшие достижения и передовые возможности. Различные качественные характеристики неизбежно вступают в конфликт друг с другом: улучшение одной часто приводит к ухудшению другой. Следовательно, важной частью анализа требований является определение наиболее важных характеристик, чтобы дизайнеры могли учесть это.
Измерения качества
Команды разработчиков программного обеспечения при изучении требований должны учитывать широкий набор атрибутов качества. Атрибуты качества также называются факторами качества и требованиями к качеству обслуживания. Термины Design for Excellence и DfX (проектирование с целью добиться наилучших характеристик) также относятся к атрибутам качества, где X — это свойство, которое разработчики стремятся оптимизировать (Wikipedia, 2021a). Говоря о нефункциональных требованиях, люди обычно имеют в виду атрибуты качества.
Нефункциональные требования не реализуются напрямую в программном или аппаратном обеспечении. Они служат лишь источником производной функциональности, архитектурных решений или подходов к проектированию и реализации. Некоторые нефункциональные требования ограничивают выбор вариантов, доступных дизайнеру или разработчику. Например, требование функциональной совместимости может ограничивать разработку продукта тем, что приходится использовать определенные стандартные интерфейсы.
Я видел списки из более чем 50 атрибутов качества, организованных в группы и иерархии. Далеко не во всех проектах приходится беспокоиться о таком их количестве. В табл. 3.1 перечислены атрибуты качества, которые каждая команда разработчиков программного обеспечения должна учитывать, изучая значение понятия качества для их продукта (Wiegers, Beatty, 2013). Физические продукты, содержащие встроенное программное обеспечение, имеют некоторые дополнительные атрибуты качества, например перечисленные в табл. 3.2 (Koopman, 2010; Sas, Avgeriou, 2020).
По аналогии с функциональностью разработчики должны сбалансировать ценность некоего качества и стоимость его достижения.
В этих двух таблицах отсутствует одно важное свойство — стоимость. По аналогии с функциональностью, разработчики должны сбалансировать ценность некоего качества и стоимость его достижения. Например, всем хотелось бы, чтобы программное обеспечение всегда было доступно для использования, но достижение этого может стоить очень дорого.
У одного из моих клиентов была компьютерная система управления производством с требованием доступности 24/7 с допустимым нулевым временем простоя. Разработчики выполнили это требование, добавив резервную систему. Для обновления программного обеспечения они сначала устанавливали все необходимое в автономной системе, выполняли тестирование, переключали эту систему в оперативный режим, а затем обновляли вторую систему. Иметь две независимые компьютерные системы — дорогое удовольствие, но это дешевле, чем останавливать производство продукта, когда система управления выходит из строя.
Определение атрибутов качества
Разработчики должны знать, какие атрибуты качества наиболее важны, какие аспекты этих часто многомерных атрибутов имеют первостепенное значение, а также целевые характеристики. Недостаточно просто сказать: «Система должна быть надежной» или «Система должна быть удобной для пользователя». На этапе выявления требований бизнес-аналитик должен выяснить, что именно заинтересованные стороны имеют в виду под надежностью или удобством. По каким характеристикам можно судить о надежности и удобстве системы? Какие примеры ненадежности или неудобства можно привести?
Чем точнее бизнес-аналитик сформулирует ожидания заинтересованных сторон в отношении качества, тем легче разработчикам будет сделать правильный выбор и оценить достижение целевых показателей. Роксана Миллер (Roxanne Miller, 2009) приводит множество примеров четко сформулированных требований к атрибутам качества в многочисленных категориях. Когда это возможно, формулируйте цели в области качества измеримыми и проверяемыми способами. Рассмотрите возможность использования Planguage — языка ключевых слов, позволяющего количественно определить такие расплывчатые атрибуты, как доступность и производительность (Simmons, 2001; Gilb, 2005). Для такой тщательной проработки требований нужно время, но оно будет потрачено с пользой по сравнению с переделкой продукта после того, как он не оправдает ожиданий клиентов.
Проектирование для качества
Разработчики могут оптимизировать свой подход к решению для практически любого параметра качества в зависимости от степени его важности. Без руководства со стороны один разработчик может оптимизировать производительность, другой — удобство использования, а третий — переносимость между платформами. Чтобы этого не происходило, на этапе исследования требований к проекту нужно определить наиболее важные атрибуты, чтобы направлять усилия разра ботчиков туда, где они наиболее важны для успеха бизнеса. А это означает, что необходимо расставлять приоритеты для нефункциональных требований по аналогии с функциональными.
Приоритизация нужна для согласования компромиссов между определенными парами атрибутов качества. Увеличение одного атрибута часто требует от разработчика пойти на компромисс в некоторых других областях (Wiegers, Beatty, 2013). Приведу несколько примеров конфликтов атрибутов качества, которые требуют компромиссных решений.
- Многофакторная аутентификация более надежна, чем простой пароль для входа, но снижает удобство использования из-за дополнительных шагов и, возможно, задействованных устройств.
- Продукт или компонент, предназначенный для повторного использования, может оказаться менее эффективным, чем если бы код, реализующий необходимую функциональность, был оптимизирован для одного приложения. При условии приемлемости потери производительности разумнее отдать предпочтение повторно используемым компонентам.
- Оптимизация системы в целях повышения производительности может ухудшить ее переносимость, если разработчики будут использовать определенные свойства операционной системы или языка, чтобы достичь максимальной производительности.
- Оптимизация одних аспектов сложного атрибута качества может привести к ухудшению других. Например, внедрение решений, упрощающих обучение новых или случайных пользователей, может сделать систему неудобной для экспертов.
- доступность (если система не дает сбоев, то остается доступной для использования);
- целостность (снижается риск потери или повреждения данных из-за сбоя);
- устойчивость (меньше вероятность отказа продукта из-за неожиданного действия пользователя или состояния окружающей среды);
- безопасность (если механизмы безопасности продукта работают надежно, то никто не пострадает).
Архитектура и атрибуты качества
Чтобы выбрать правильную архитектуру, команда разработчиков должна заранее понять, какие атрибуты требуют пристального внимания. Архитектура влияет на множество атрибутов, в том числе доступность, эффективность, функциональную совместимость, производительность, переносимость, надежность, безопасность, масштабируемость и защищенность. Поскольку необходимость компромиссов — частое явление, архитекторы должны знать, какие атрибуты наиболее важны, иначе не смогут принять решения, ведущие к желаемым результатам.
Возвращение на поздних стадиях разработки или после выпуска и переделка архитектуры системы в рамках исправления недостатков качества — дорогое удовольствие. Поэтапное построение систем при отсутствии предварительного знания наиболее важных целей в области качества может привести к проблемам, которые трудно исправить, особенно если речь идет не только о программном, но и об аппаратном обеспечении. Как это часто бывает с программными проектами, потратив чуть больше времени на то, чтобы лучше понять цели в области качества, можно прийти к менее дорогим и более надежным решениям.
Проблемы легче предупредить, чем исправить
Начиная писать свою первую книгу 25 лет назад, я плохо понимал, что делаю. Я начал с комически скудного наброска; в первоначальной структуре моей книги было много ошибок. Под руководством чрезвычайно терпеливого редактора (спасибо, Венди!) я изменил структуру рукописи, сделав ее более читабельной. На копирование, исправление и приглаживание фрагментов текста ушел целый месяц. Ценность содержимого не увеличилась, но подача значительно улучшилась.
Этот болезненный опыт крепко врезался мне в память. С тех пор как я начал вкладывать гораздо больше сил в организацию книги на архитектурном и детальном уровнях, мне никогда не приходилось совершать крупных переделок, разве что незначительно менять последовательность изложения. Благодаря этому я могу сосредоточиться на содержании, а не структуре. Как мы видели в уроке 18 «Чем выше уровень абстракции, тем проще выполнять итерации», перемещать элементы в макете книги гораздо проще, чем реорганизовывать и переписывать предложения.
Тот же урок применим к проектированию программного обеспечения. Время, потраченное на вдумчивое рассмотрение проекта, с лихвой окупается временем, которое не было потрачено на исправление проблем позже. Вам может понадобиться дополнительное время на то, чтобы усовершенствовать проект в условиях неопределенности, поэтому старайтесь распределять усилия в соответствии с характером проблемы. Даже приложив максимум усилий для создания безупречного проектного решения, позднее вы можете обнаружить недостатки и вам придется их корректировать.
Технический долг и рефакторинг
Проекты, выполненные в спешке, могут привести к появлению технического долга — так называют недостатки, которые кто-то должен устранить в будущем, чтобы обеспечить надлежащее функционирование продукта и его расширяемость. (Подробнее о техническом долге см. в уроке 50 «Сегодняшний проект, требующий немедленной реализации, завтра может превратиться в кошмар для службы сопровождения».) Небольшой технический долг может быть приемлемым компромиссом, если экономия на проектировании и разработке кода ускоряет достижение текущей бизнес-цели. Однако недостатки остаются. Чем дольше команда откладывает их решение, тем более масштабной, дорогостоящей и разрушительной будет доработка. Как и любой кредит, технический долг следует рассматривать как временный и требующий постепенного погашения.
Доработка в целях уменьшения технического долга часто принимает форму рефакторинга — процесса реорганизации существующего кода для улучшения его структуры без изменения функциональности. Вы можете упростить код, сделать его более удобным для сопровождения или расширения, повысить его эффективность, удалить повторяющиеся и ненужные части или внести другие улучшения.
Существенные изменения проекта могут потребовать значительных усилий по реорганизации, тогда как команды обычно предпочитают создавать новые полезные функции. Мне становится не по себе, когда я вижу, что приставка re- (пере-) используется слишком часто. Она означает, что мы снова делаем то, что уже однажды делали.
Внесение изменений в проект требует усилий, притом что уровень ценности для клиента остается прежним, но этот труд помогает поддерживать стабильную основу, необходимую для дальнейшего развития продукта. Хорошее проектирование сводит к минимуму возникающий технический долг, а рефакторинг устраняет его. Разумный баланс этих двух факторов дает наилучшие результаты. Недостаточное проектирование может привести к значительным доработкам; на слишком детальную проработку проекта может потребоваться чересчур много времени, при этом хороший результат не гарантирован. Два высказывания экспертов по проектированию раскрывают эту двойственность:
Постоянное совершенствование проектного решения упрощает работу с кодом. Однако на
практике обычно выбирается другой путь: небольшой рефакторинг и большое внимание к
целесообразности добавления новых функций (Kerievsky, 2005).
Продумать или узнать все в начале проекта практически невозможно… Однако вы можете
использовать свой опыт и опыт других, чтобы выбрать определенное направление. Сегодня
вы можете принимать решения, которые завтра минимизируют потребность внесения
изменений (Pugh, 2005).
Как указывает Кен Пью в предыдущей цитате, цель проектирования состоит в том, чтобы принять разумные решения сейчас и предотвратить ненужные изменения в будущем. Опираясь на свои рассуждения и мнение заинтересованных сторон, выбирайте такие решения, которые уменьшат вероятность появления необходимости изменять те или иные части продукта в будущем.
Накопление технического долга из-за того, что у команды нет времени на надлежащее проектирование, просто отодвигает проблему в будущее.
Архитектурные недостатки
Внесение небольших изменений в проект по мере продвижения — не слишком болезненный процесс, при этом продукт непрерывно и постепенно улучшается. Серьезная реорганизация архитектуры ради увеличения устойчивости продукта или улучшения пользовательского опыта дастся гораздо сложнее.
Примером несовершенного архитектурного проектирования, влияющего на опыт пользователя, может служить разнообразие способов удаления единицы информации из смартфона. Действия пользователя, подсказки и значки различаются в зависимости от того, что удаляется: текстовое или почтовое сообщение, сохраненное местоположение на карте, фотография, заметка, событие календаря, будильник, контакт, пропущенный телефонный звонок или целое приложение. Одни операции удаления требуют подтверждения, другие — нет. Иногда процесс может немного меняться в зависимости от того, удаляется один экземпляр объекта или несколько. Эти отличия могут сбивать пользователя с толку.
Дизайнеры могли бы избежать многих несоответствий, применяя в работе общие стандарты организации пользовательского интерфейса и дизайна архитектуры в целом, как, например, повторное использование некоего кода. Повторное использование — отличный способ улучшить качество, повысить производительность разработчиков и сократить время обучения пользователя. Унификация операций удаления на столь поздней стадии развития продукта потребовала бы чрезмерного объема работы. И это только для одной операции, которая присутствует в той или иной форме почти в каждой программной системе.
Разработчики ПО всегда создают проект либо в процессе работы, либо в ходе тщательного обдумывания. Накопление технического долга из-за того, что у команды нет времени на надлежащее проектирование, просто отодвигает проблему в будущее, где ее влияние продолжает расти. Инвестирование в проектное решение (то, что Кен Пью называет префакторингом (prefactoring) (Pugh, 2005)) позволяет сэкономить силы и время на реструктуризацию и повторную реализацию в будущем, когда предпочтительнее работать над чем-то другим.
Об авторе
Карл Вигерс (Karl Wiegers) — С 1997 года занимает пост главного консультанта в Process Impact, консалтинговой и обучающей компании в сфере разработки программного обеспечения, находящейся в Хэппи-Вэлли, штат Орегон. До этого Карл 18 лет работал в Kodak, где занимал должности ученого-исследователя фотографического дела, разработчика программного обеспечения, администратора программного обеспечения и, наконец, руководителя процесса разработки и совершенствования программного обеспечения. Карл получил степень доктора органической химии в Университете штата Иллинойс.
Карл — автор 12 книг, в том числе The Thoughtless Design of Everyday Things, Software Requirements1, More About Software Requirements, Practical Project Initiation, Peer Reviews in Software, Successful Business Analysis Consulting, и детективного романа под названием The Reconstruction. Он написал множество статей по разработке программного обеспечения, менеджменту, проектированию, консалтингу, химии и военной истории. Несколько книг Карла были отмечены наградами, одна из последних — премия Общества технических коммуникаций за книгу Software Requirements, написанную в соавторстве с Джой Битти. Карл работал в редакции журнала IEEE Software и писал статьи для журнала Software Development.
Когда он не сидит за компьютером, то любит дегустировать вина, работать волонтером в публичной библиотеке, играть на гитаре, сочинять и записывать песни, читать книги о военной истории и путешествиях. Вы можете связаться с ним через www.processimpact.com или www.karlwiegers.com.
Карл — автор 12 книг, в том числе The Thoughtless Design of Everyday Things, Software Requirements1, More About Software Requirements, Practical Project Initiation, Peer Reviews in Software, Successful Business Analysis Consulting, и детективного романа под названием The Reconstruction. Он написал множество статей по разработке программного обеспечения, менеджменту, проектированию, консалтингу, химии и военной истории. Несколько книг Карла были отмечены наградами, одна из последних — премия Общества технических коммуникаций за книгу Software Requirements, написанную в соавторстве с Джой Битти. Карл работал в редакции журнала IEEE Software и писал статьи для журнала Software Development.
Когда он не сидит за компьютером, то любит дегустировать вина, работать волонтером в публичной библиотеке, играть на гитаре, сочинять и записывать песни, читать книги о военной истории и путешествиях. Вы можете связаться с ним через www.processimpact.com или www.karlwiegers.com.
Более подробно с книгой можно ознакомиться на сайте издательства:
» Оглавление
» Отрывок
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Для Хаброжителей скидка 25% по купону — Разработка