Ненужная сложность: почему мы неправильно проектируем программное обеспечение
Я прихожу к выводу, что есть два способа создания проекта программного обеспечения: один способ — сделать его настолько простым, чтобы явно не было недостатков, а другой — сделать его настолько сложным, чтобы не было очевидных недостатков. — Тони Хоар
Простота. Святой Грааль программиста. К нам неоднократно призывали те, кто занимал высокие посты:
- (Разработка программного обеспечения) это ремесло … и оно во многом связано с ценить простоту выше сложности — Барбара Лисков
- Нам нужно создавать простые системы, если мы хотим создавать хорошие системы, — Рич Хики
- Единственный способ написать сложное программное обеспечение, которое не потерпит неудачу, — это сдержать его глобальную сложность, построить его из простых частей — Эрик С. Реймонд
- Простое может быть сложнее, чем сложное: вам нужно много работать, чтобы ваше мышление стало чистым, чтобы сделать его простым. Но, в конце концов, оно того стоит, потому что как только вы доберетесь туда, вы сможете свернуть горы. — Стив Джобс
- Простота — необходимое условие надежности. — Эдсгер Дейкстра.
- Цена надежности — это стремление к максимальной простоте — Тони Хоар
И все же, как и Грааль, простота оказывается неуловимой. Есть много причин для этого:
- Программное обеспечение по сути сложное. Элементы программного обеспечения взаимодействуют друг с другом нелинейным образом. Сложность в целом увеличивается гораздо больше, чем линейно.
- У вас не всегда есть контроль над программным обеспечением, которое вы должны использовать (например, сторонние библиотеки, библиотеки из других групп в вашей компании, устаревшие библиотеки и т. Д.)
- Постоянное давление на перемены.
- Конкурирующие потребности и интересы часто противоречат друг другу. (1)
Сложность в целом возрастает гораздо больше, чем линейно.
Нетехнические факторы
Большая часть сложности, с которой [программист] должен справиться, — это произвольная сложность, вынужденная без всяких рифм и причин множеством человеческих институтов и систем, которым должны соответствовать его интерфейсы. — Фред Брукс
Всякий раз, когда мы думаем о программном обеспечении, мы должны думать не только о технических аспектах. Есть причины, по которым он существует, и они не имеют ничего общего с кодом.
Некоторые из этих вещей включают:
- Пользователи
- Бизнес и заинтересованные стороны
- Потребности / интересы инженеров, дизайнеров, владельцев продуктов
- Финансовые ограничения
Во многих случаях причиной этих осложнений являются личные интересы или политика. Хотя нам это может не нравиться, это реальные факторы, которые влияют на разработку программного обеспечения. Мы игнорируем их влияние и сложность на свой страх и риск.
Сложность неизбежна
В конечном итоге каждая программа превращается в рококо, а затем в руины — Алан Перлис
В любом значимом программном обеспечении неизбежны сложности. Программные системы должны развиваться, иначе они устареют. По мере развития они неизбежно добавляют сложность в виде новых или измененных функций. Итак, как выясняется, в наших интересах усложнять наше программное обеспечение!
Простота не может быть результатом простого мышления
В отрасли, изобилующей сложностями, было бы очень удобно, если бы существовал способ гарантировать хорошее программное обеспечение. Действительно, многие будут утверждать, что существует магическая техника или метод. Давайте рассмотрим некоторые из них:
Статические типы решат все!
В последнее время много шумихи и много усилий было вложено в использование статических типов как спасителя качества программного обеспечения. Например, процитируем сайт Elm:
В отличие от написанного вручную JavaScript, код Elm на практике не создает исключений во время выполнения. Вместо этого Elm использует вывод типов для обнаружения проблем во время компиляции и дает дружественные подсказки. Таким образом, проблемы никогда не дойдут до ваших пользователей.
Это прекрасное чувство. Если бы это было правдой. Было бы замечательно, если бы правильное использование статических типов могло решить все наши проблемы.
Конечно, если бы это было правдой, для каждого разработчика программного обеспечения было бы моральным долгом использовать такой язык, как Elm.
Воздействие было бы настолько значительным, что Земля была бы настолько жизненно важной, что не было бы никаких споров. К сожалению, это не так.
Фанатики статической типизации пытаются заставить нас поверить в то, что «хорошо типизированные программы не могут ошибаться». Хотя это, безусловно, звучит впечатляюще, это довольно бессмысленное заявление.
Статическая проверка типов — это абстракция во время компиляции поведения вашей программы во время выполнения, и, следовательно, она обязательно только частично правильная и неполная.
Это означает, что программы по-прежнему могут работать неправильно из-за свойств, которые не отслеживаются средством проверки типов, и что есть программы, которые, хотя и не могут выйти из строя, не могут быть проверены типом. — Эрик Мейер и Питер Дрейтон, из Конец холодной войны между языками программирования
Фактически, как отмечают Мейер и Дрейтон, чрезмерный упор на статические типы может вызвать проблемы из-за повышенной сложности, которую они вносят:
Стремление сделать статическую типизацию менее частичной и более полной приводит к тому, что системы типов становятся чрезмерно сложными и экзотическими, о чем свидетельствуют такие концепции, как «фантомные типы» и «шаткие типы». Это похоже на попытку пробежать марафон с мячом и цепью, привязанным к ноге, и торжествующий крик, что вы почти сделали это, хотя вы выскочили после первой мили.
Данные о плотности ошибок позволяют предположить, что их выводы верны. Корреляция между языками и более низкой плотностью ошибок более точно соответствует по простоте, чем статические типы:
Как отмечает Дэн Лебреро, автор процитированного выше исследования: «[данные] не показывают никаких доказательств того, что статическая / динамическая типизация имеет какое-либо значение, но они действительно показывают… разрыв между языками, которые ориентированы на простоту, и языками, которые не . »
Вам просто нужно использовать правильную структуру
Язык программирования — это низкоуровневый язык, когда его программы требуют внимания к несущественному. — Алан Перлис
Индустрия программного обеспечения постоянно работает над созданием уровней абстракции, которые делают кодирование более простым и надежным.
Тот факт, что мы больше не программируем на машинном языке, свидетельствует об успехе этого начинания. Хотя мы устранили множество случайных сложностей, основная сложность, присущая системам, осталась.
А построенные нами абстракции порождают новые проблемы.
Как классно заметил Джоэл Спольски: «Все нетривиальные абстракции в некоторой степени дырявые». Под «дырявым» он подразумевает, что абстракция каким-то образом не справляется со сложностью того, для чего она была разработана. Сложность по-прежнему «просачивается» через трещины в абстракции.
Мы можем увидеть современный пример этого с React JS. Одно из основных намерений React — абстрагироваться от DOM. Однако React предоставляет refs для тех случаев, когда вам может понадобиться взаимодействовать с DOM.
Для этого, конечно, вы должны понимать, как взаимодействовать с DOM. Это означает, что абстракция не удалась.
Фактически, это вносит дополнительную сложность в понимание того, как избежать React, чтобы взаимодействовать с DOM.
Кроме того, существуют сложности, связанные с абстракцией. Абстракции по необходимости скрывают детали реализации и, следовательно, накладывают ограничения на то, что может быть достигнуто. Это причина, по которой в React есть refs. Есть вещи, на которые он просто не способен. Сама по себе абстракция может иметь определенные сложности.
Серебряной пули нет
Хотя приведенные выше комментарии могут вызвать опасения религиозных споров, они не предназначены для этого. Скорее, цель состоит в том, чтобы указать:
Нет ни одной разработки ни в технологии, ни в технике управления, которая сама по себе обещала бы хотя бы на один порядок повышения производительности [программного обеспечения], надежности и простоты. — Фред Брукс
Однако, в отличие от попадания в ад Данте, вам не нужно терять всякую надежду.
Осознание того, что не существует волшебного метода создания совершенного программного обеспечения, выводит вас из царства магического мышления.
Только когда вы выйдете из этой фантазии, вы сможете иметь дело с реальностью программного обеспечения. И как только вы окажетесь там, вы увидите, что дело не в программном обеспечении.
Оно больше: оно личное и межличностное. Это процедурно. Это среда и ситуация. Это временное и историческое. Это сразу несколько поколений. Другими словами … Речь идет о системах.
Что делать, если Silver Bullet нет? Мы можем согласиться с тем, что:
- Нам всегда будет сложно справиться.
- Мы не будем писать идеальное программное обеспечение — и будем с этим мириться.
- Всегда будут компромиссы и несовершенства.
- Хорошая работа требует времени.
- Мы должны иметь дело с человеческими факторами (предпочтениями, политикой, разными способами мышления и т. Д.).
… Дизайн — это то, что лучше всего делать медленно и осторожно — Алан Кей
Что делает это простым?
[Простота программного обеспечения] требует тех же навыков, преданности делу, проницательности и даже вдохновения, что и открытие простых физических законов, лежащих в основе сложных явлений природы.
Это также требует готовности принять цели, которые ограничены физическими, логическими и технологическими ограничениями, и пойти на компромисс, когда противоречащие цели не могут быть достигнуты.
— Тони Хоар
Трудно определить, что делает что-то простым. Отчасти это связано с тем, что простота в некоторой степени субъективна. Частично это связано с тем, что интерпретация простоты зависит от опыта.
Что-то простое и очевидное для подготовленного математика может оказаться не таким простым для биолога — и наоборот.
Это оставляет у нас в лучшем случае смутное понимание простоты, которое напоминает знаменитое определение порнографии судьей Поттером: «Я узнаю это, когда вижу».
Тем не менее, вот некоторые аспекты простой программы:
- Понимание и решение проблем становятся управляемыми, если хорошо поняты принципы, лежащие в основе системы.
- Система использует хорошо известные культурные маркеры для облегчения понимания.
- Он поддерживает согласованность дизайна.
- Он хорошо использует семантику.
- Он уравновешивает потребности гибкости с границами и областью действия системы.
- Сложность максимально ограничена.
- Выявляется и устраняется ненужная сложность.
Защита от ненужной сложности
Хотя есть вещи, которые отдельные разработчики могут сделать, чтобы предотвратить сложность, наибольшее влияние оказывают усилия на уровне организации. Вот несколько вещей, которые могут сделать организации:
Поспешишь — людей насмешишь
Когда какой-либо… проект приближается к завершению, всегда возникает безумная спешка, чтобы добавить новые функции… Эта спешка действительно безумна, потому что ведет в ловушку… — Тони Хоар
Стремитесь убедить отдел продаж, бизнеса и продукта в том, что разработка программного обеспечения — разработка хорошего программного обеспечения — требует времени.
- Дайте время на дизайн.
- Выделите время для хороших практик программирования.
- Дайте время на рефакторинг.
У каждой жертвы есть цена. Эти затраты со временем увеличиваются.
Избегайте новейших технических ловушек
Разработка программного обеспечения — это индустрия моды.
Погоня за последним и величайшим порождает хаос, поскольку программисты изо всех сил пытаются изучить методологию, язык или структуру, которые, наконец, позволят им делать вещи «правильным» образом.
Внимательность и готовность не следовать за поводом просто потому, что он популярен, могут иметь большое значение для защиты вашего кода.
Проверено намного лучше, чем круто.
Следуйте стандартным процедурам
Программистов следует оценивать не по их изобретательности и логике, а по полноте анализа их случаев. — Алан Перлис
Есть стандартные, хорошо известные, проверенные на практике методы, которые помогают повысить качество и простоту программного обеспечения:
- Code Review — возможно, один из самых мощных инструментов в арсенале команды разработчиков программного обеспечения. Убедитесь, что ваши обзоры кода хорошо продуманы и хорошо выполнены.
- Обзоры дизайна — проверяйте не только код, но и дизайны.
- Достаточно заранее спроектировать — не просто садитесь и пишите код. Посмотрите, как устроена ваша система. Следуйте этим шаблонам проектирования, чтобы ваши проекты оставались неизменными с течением времени.
- Составьте грандиозный план работы вашей системы. Определите такие аспекты на высоком уровне, как: Как вы будете справляться с асинхронностью в своем приложении? Как вы будете управлять отказоустойчивостью? Как вы будете управлять согласованностью данных? и т. д.
- Убедитесь, что ваши требования хороши — как бы вы их ни выполняли.
Резюме
Простота — это далеко не просто. Также важно помнить, что это средство для достижения цели, а не сама цель. В конце концов, это качественное программное обеспечение, которым легко пользоваться и с которым легко работать.
На сегодняшний день поддержание простоты систем было одним из наиболее эффективных способов, которыми люди управляли при работе с программным обеспечением. Простота — это не только процесс и практика, но и набор принципов, указаний или философии. Это то, что должно поддерживаться и управляться всей организацией.
— — — — —
Дополнительная литература:
Надежность в программной инженерии
- Несмотря на то, что в индустрии программного обеспечения было много инноваций, достижений, сложных систем и опытных ветеранов, она еще так молода по сравнению с другими областями и практиками.
- Само создание программного обеспечения может быть тривиальным в некоторых обстоятельствах, но для того, чтобы обеспечить действительно современное качество, мы сначала должны его спроектировать, и речь идет не только об услугах, но также о командах и человеческом факторе, стоящем за этим.
- Без этого было бы сомнительно, сможем ли мы построить системы качества.
Надежность программного обеспечения
Надежность — это одна из характеристик, характеризующих качественное программное обеспечение и определяющая разницу между хорошим и плохим.
- Надежное программное обеспечение — это система, устойчивая к сбоям или даже безотказная.
- Это вероятность безотказной работы компонента в течение заданного периода времени в заданной среде.
- Есть одно высказывание, которое до сих пор не покидает меня, когда я думаю о надежных системах:
Надежность — это щелкнуть выключателем и знать, что свет загорится.
Надежность — это не еще одна метрика, а то, как клиенты воспринимают систему.
Это не статическая характеристика. Он динамичен в том смысле, что система может выдерживать сбои при различных обстоятельствах, как ожидаемых, так и неожиданных.
Устойчивость — это путь, а надежность — результат.
Система не может быть надежной, не будучи устойчивой, и наоборот.
В зависимости от того, в какой части мира вы проживаете, вы можете часто слышать, как эти два слова используются взаимозаменяемо. Тем не менее, в этом есть некоторая разница.
- Отказоустойчивость программной системы на самом деле измеряется тем, насколько хорошо и насколько она может противостоять угрозам и смягчать их, а также насколько быстро она может восстанавливаться.
- Пометка программной системы как надежной требует от нас обеспечения как работы, так и дизайна компонентов на месте.
- Независимо от отрасли, размера компании, приоритетов и клиентов, все хотят, чтобы их программные компоненты и платформы работали всегда.
Дизайн для надежности
За проектирование и создание надежного программного обеспечения несут ответственность все участники, будь то дизайнеры UI/UX, менеджеры по продуктам, инженеры, архитекторы или поставщик базовой инфраструктуры и оборудования.
Чтобы программное обеспечение было надежным, оно должно быть спроектировано таким образом, чтобы:
- Свести к минимуму сбои
- Минимизируйте влияние сбоев
- Обеспечение бесперебойной работы
Компоненты должны оставаться функциональными как в прогнозируемых, так и в непредвиденных сценариях.
Надежные системы создаются для того, чтобы им доверяли.
- На этапе проектирования услуги, компонента или продукта мы уже должны стремиться к надежности.
- Несовершенство конструкции может часто приводить к путанице и ненужной сложности, а также может стать хорошей почвой для ненадежных решений.
- На этапе концепции есть две модели надежности, которые вы можете использовать:
- Постановка цели надежности программного обеспечения
- План программы обеспечения надежности программного обеспечения
Поставьте своей целью надежность, просматривайте и анализируйте решения во время проектирования, концепции и разработки, и результаты значительно улучшатся еще до того, как вы начнете поставлять продукт.
Используйте другие методы, используемые при проектировании надежности, такие как:
- Обзоры командного дизайна
- Виды сбоев программного обеспечения и анализ последствий
- Анализ дерева ошибок программного обеспечения
- Анализ отказоустойчивости программного обеспечения
«Дизайн для надежности» сам по себе является моделью, часто являющейся частью Дизайна для совершенства.
Общие угрозы
Мы приветствуем общие угрозы, чтобы улучшить систему и спроектировать ее так, чтобы она была адаптируемой и готовой к устранению проблем.
Общий набор угроз для каждой системы может быть следующим:
- Угрозы безопасности
- Задержка
- Сильная зависимость от других сервисов
- Межсервисная связь
- Открытие
- Невосприимчивость
- Нерешенные проблемы и крайние случаи
- Изменения окружающей среды
- Аппаратные сбои
- Непоследовательность как в производительности, так и в поведении
Список очень приблизительный и может продолжаться довольно долго.
Надежность часто касается всего правильного и неправильного в программной системе. Все факторы, которые могут привести к его сбою, по сути являются угрозами, от безопасности, плохого дизайна и ограниченных аппаратных ресурсов до некорректности и непредвиденных проблем.
По опыту, сбои вызываются:
- Забытый анализ угроз
- Техническая некорректность
- Оверинжиниринг и сверхдизайн
- Ошибки инфраструктуры
- Отсутствие тестирования
- Низкая устойчивость к изменениям разработки
Есть причина, по которой отличное программное обеспечение легко поддерживать, оно простое, эффективное и надежное. Но это должно быть спроектировано и построено таким образом.
Отсутствие способности восстанавливаться после сбоев также является важным фактором и очень часто влияет на надежность системы.
Преодоление препятствий
Тысячи раз все мы сталкиваемся с программным обеспечением, которое просто не работает.
Он не ломается, не выбрасывает флаг, уведомляющий вас об ошибке — но почему-то просто не делает того, для чего явно предназначен. Это подразумевает несправедливость, плохой дизайн и нулевое ожидание его поведения и кричит: «Я плохо делаю то, что должен делать».
Надежность заложена в продукте и процессах.
Перспектива решает все. Во-первых, посмотрите на свое программное обеспечение с точки зрения пользователя. Используйте, пробуйте — займите место потребителя.
- Улучшите свой продукт в техническом смысле, но затем поработайте над своими процессами и посмотрите, что там тоже можно улучшить.
- Всегда анализируйте свое программное обеспечение, функции, угрозы, ошибки, инциденты и проблемы.
- Технически все следующие методы и концепции должны быть включены, чтобы мы могли улучшить наше программное обеспечение:
- Ведение журнала
- Аудит
- Метрики инфраструктуры
- Показатели аппаратных ресурсов
- Пользовательские метрики
- Оповещение в реальном времени
- Аналитика
- Посмертный анализ
- Отчет об инцидентах
- Управление происшествиями
Надежное программное обеспечение не создается и не проверяется за один день. Надежность должна быть достигнута, и то, как вы проектируете, строите и улучшаете свои системы, является частью этого.
Виды отказов и анализ последствий
Существует множество фреймворков и моделей, которые помогают преодолевать проблемы, анализировать и улучшать решения.
Одним из них является анализ видов и последствий отказов. FMEA представляет собой систематическую и упреждающую основу для выявления возможных сбоев и их последствий.
- Это процесс проверки компонентов и частей, составляющих вашу систему, который позволяет определить причины и последствия сбоев.
- FMEA является основной задачей проектирования надежности.
- Многие компании и сообщества не имеют таких моделей, но стремятся к высокому качеству, стабильности и надежности.
- Он просто позволяет вам распознать:
- Что может пойти не так
- Почему может произойти сбой
- Каковы влияние и последствия сбоя
Метрики инцидентов
Сбои, сбои и ошибки — все это приводит к простоям и влияет на надежность системы.
Отслеживание метрик, связанных с инцидентами, является обязательным. Управление инцидентами, их обнаружение, диагностика, разрешение и предотвращение — все это ключевые показатели эффективности вашего инженерного отдела.
Что касается KPI, то вот лишь некоторые из них:
- Количество оповещений, созданных за определенный период времени
- Количество инцидентов, произошедших за определенный период времени
- Среднее время наработки на отказ
- Среднее время подтверждения
- Среднее время обнаружения
- Среднее время решения
Только после того, как вы это сделаете, вы действительно сможете понять, какие сегменты можно улучшить, будь то команда, продукт, услуга или весь уровень вашего инженерного отдела.
Ротация по вызову
- Независимо от того, есть ли у вас специальная группа поддержки или ваши разработчики поддерживают внутренние компоненты, вам нужна ротация по вызову.
- Доказано, что это повышает качество и надежность вашего программного обеспечения.
- Наличие метрики времени, затраченного на обработку инцидентов во время дежурства, также является очень полезным способом получить больше информации.
Тем не менее, все ключевые показатели эффективности, измерения и метрики в мире не могут заменить контекст. Инциденты уникальны, и статистика иногда оказывается ловушкой.
Контекстно-зависимый анализ инцидента является обязательным и часто требует лишь щепотки здравого смысла.
Оповещение
Иметь метрики для анализа ваших компонентов — это здорово, иметь оповещения в реальном времени еще до того, как клиент заметит проблему, — это потрясающе.
Надежность заложена не только в программном обеспечении, но и в командах, стоящих за ним.
Оповещение и реагирование на проблемы необходимы для обеспечения надежности для потребителей. Ключевым моментом является смягчение последствий — обнаружение проблемы до того, как она разразится, и ваши клиенты даже заметят ее, позволит вам нарисовать совершенно другую картину и позволит вам предотвратить инциденты.
Оповещение обязательно. Исходя из опыта, вы удивитесь, сколько преимуществ дает оповещение. Попробуйте и посмотрите, какие каналы лучше всего подходят для вас:
- Страницы статуса в реальном времени
- Электронная почта
- SMS
- Slack, Teams или что вы используете
Многие успешные компании имеют все это на месте. Оповещение достойно того, чтобы стать инициативой всей компании вместе с мониторингом.
Методика «шести сигм
Design for Six Sigma, или DFSS — это системный подход и модель в программной инженерии, целью которой является достижение Надежности. Это процесс создания высокого и улучшенного качества.
«Шесть сигм» позволит вам:
- Статистический контроль качества
- Методический подход
- Подход, основанный на фактах и данных
- Ориентация на проект и цель
- Ориентация на клиента
Все это является необходимым условием для надежного программного обеспечения. Это позволяет вам, в свою очередь, включить цикл DMAIC, сокращение от Design-Measure-Analyse-Improve-Control.
Существует множество концепций, факторов и аспектов надежности программного обеспечения, но, надеюсь, вышеизложенное суммирует большую часть этого. Цель этой статьи — пролить свет на уже устоявшиеся концепции, а также на некоторые менее известные. Надежность программного обеспечения — это только один из разделов инженерии, но, по сути, это отдельная наука.
Надеюсь, вам понравилась эта история, и вы нашли ее интересной, независимо от того, являетесь ли вы инженером, архитектором, менеджером или дизайнером. Следите за новостями и подписывайтесь на мою рассылку, чтобы быть в курсе других историй, подобных этой.
Спасибо за чтение! ????
1.2. Почему программному обеспечению присуща сложность?
Как
говорит Брукс, «сложность программного
обеспечения — отнюдь не случайное его
свойство» [3].
Сложность вызывается четырьмя основными
причинами:
- сложностью реальной предметной области, из которой исходит заказ на разработку;
- трудностью управления процессом разработки;
- необходимостью обеспечить достаточную гибкость программы;
- неудовлетворительными способами описания поведения больших дискретных систем.
Сложность реального
мира. Проблемы,
которые мы пытаемся решить с помощью
программного обеспечения, часто неизбежно
содержат сложные элементы, а к
соответствующим программам предъявляется
множество различных, порой взаимоисключающих
требований.
Рассмотрим необходимые
характеристики электронной системы
многомоторного самолета, сотовой
телефонной коммутаторной системы и
робота. Достаточно трудно понять, даже
в общих чертах, как работает каждая
такая система.
Теперь прибавьте к этому
дополнительные требования (часто не
формулируемые явно), такие как удобство,
производительность, стоимость,
выживаемость и надежность! Сложность
задачи и порождает ту сложность
программного продукта, о которой пишет
Брукс.
Эта
внешняя сложность обычно возникает
из-за «нестыковки» между пользователями
системы и ее разработчиками: пользователи
с трудом могут объяснить в форме, понятной
разработчикам, что на самом деле нужно
сделать. Бывают случаи, когда пользователь
лишь смутно представляет, что ему нужно
от будущей программной системы.
Это в
основном происходит не из-за ошибок с
той или иной стороны; просто каждая из
групп специализируется в своей области,
и ей недостает знаний партнера. У
пользователей и разработчиков разные
взгляды на сущность проблемы, и они
делают различные выводы о возможных
путях ее решения.
На самом деле, даже
если пользователь точно знает, что ему
нужно, мы с трудом можем однозначно
зафиксировать все его требования. Обычно
они отражены на многих страницах текста,
«разбавленных» немногими рисунками.
Такие документы трудно поддаются
пониманию, они открыты для различных
интерпретаций и часто содержат элементы,
относящиеся скорее к дизайну, чем к
необходимым требованиям разработки.
Дополнительные
сложности возникают в результате
изменений требований к программной
системе уже в процессе разработки. В
основном требования корректируются
из-за того, что само осуществление
программного проекта часто изменяет
проблему.
Рассмотрение первых результатов
— схем, прототипов, — и использование
системы после того, как она разработана
и установлена, заставляют пользователей
лучше понять и отчетливей сформулировать
то, что им действительно нужно.
В то же
время этот процесс повышает квалификацию
разработчиков в предметной области и
позволяет им задавать более осмысленные
вопросы, которые проясняют темные места
в проектируемой системе.
Большая
программная система — это крупное
капиталовложение, и мы не можем позволить
себе выкидывать сделанное при каждом
изменении внешних требований.
Тем не
менее даже большие системы имеют
тенденцию к эволюции в процессе их
использования: следовательно, встает
задача о том, что часто неправильно
называют сопровождением
программного обеспечения.
Чтобы быть более точными, введем несколько
терминов:
- под сопровождением понимается устранение ошибок;
- под эволюцией — внесение изменений в систему в ответ на изменившиеся требования к ней;
- под сохранением — использование всех возможных и невозможных способов для поддержания жизни в дряхлой и распадающейся на части системе.
К сожалению, опыт
показывает, что существенный процент
затрат на разработку программных систем
тратится именно на сохранение.
Трудности
управления процессом разработки.
Основная задача разработчиков состоит
в создании иллюзии простоты, в защите
пользователей от сложности описываемого
предмета или процесса.
Размер исходных
текстов программной системы отнюдь не
входит в число ее главных достоинств,
поэтому мы стараемся делать исходные
тексты более компактными, изобретая
хитроумные и мощные методы, а также
используя среды разработки уже
существующих проектов и программ.
Однако
новые требования для каждой новой
системы неизбежны, а они приводят к
необходимости либо создавать много
программ «с нуля», либо пытаться
по-новому использовать существующие.
Всего 20 лет назад программы объемом в
несколько тысяч строк на ассемблере
выходили за пределы наших возможностей.
Сегодня обычными стали программные
системы, размер которых исчисляется
десятками тысяч или даже миллионами
строк на языках высокого уровня. Ни один
человек никогда не сможет полностью
понять такую систему. Даже если мы
правильно разложим ее на составные
части, мы все равно получим сотни, а
иногда и тысячи отдельных модулей.
Поэтому такой объем работ потребует
привлечения команды разработчиков, в
идеале как можно меньшей по численности.
Но какой бы она ни была, всегда будут
возникать значительные трудности,
связанные с организацией коллективной
разработки.
Чем больше разработчиков,
тем сложнее связи между ними и тем
сложнее координация, особенно если
участники работ географически удалены
друг от друга, что типично в случае очень
больших проектов. Таким образом, при
коллективном выполнении проекта главной
задачей руководства является поддержание
единства и целостности разработки.
Гибкость
программного обеспечения.
Домостроительная компания обычно не
имеет собственного лесхоза, который бы
ей поставлял лес для пиломатериалов;
совершенно необычно, чтобы монтажная
фирма соорудила свой завод для изготовления
стальных балок под будущее здание.
Однако в программной индустрии такая
практика — дело обычное. Программирование
обладает предельной гибкостью, и
разработчик может сам обеспечить себя
всеми необходимыми элементами,
относящимися к любому уровню абстракции.
Такая гибкость чрезвычайно соблазнительна.
Она заставляет разработчика создавать
своими силами все базовые строительные
блоки будущей конструкции, из которых
составляются элементы более высоких
уровней абстракции.
В отличие от
строительной индустрии, где существуют
единые стандарты на многие конструктивные
элементы и качество материалов, в
программной индустрии таких стандартов
почти нет. Поэтому программные разработки
остаются очень трудоемким делом.
Проблема
описания поведения больших дискретных
систем. Когда
мы кидаем вверх мяч, мы можем достоверно
предсказать его траекторию, потому что
знаем, что в нормальных условиях здесь
действуют известные физические законы.
Мы бы очень удивились, если бы, кинув
мяч с чуть большей скоростью, увидели,
что он на середине пути неожиданно
остановился и резко изменил направление
движения [Даже простые непрерывные
системы могут иметь сложное поведение
ввиду наличия хаоса. Хаос привносит
случайность, исключающую точное
предсказание будущего состояния системы.
Например, зная начальное положение двух
капель воды в потоке, мы не можем точно
предсказать, на каком расстоянии друг
от друга они окажутся по прошествии
некоторого времени. Хаос проявляется
в таких различных системах, как атмосферные
процессы, химические реакции, биологические
системы и даже компьютерные сети.
К
счастью, скрытый порядок, по-видимому,
есть во всех хаотических системах, в
виде так называемых аттракторов].
В недостаточно отлаженной программе
моделирования полета мяча такая ситуация
легко может возникнуть.
Проблемы разработки программного обеспечения
- Тенденции развития современных информационных технологий определяют постоянное возрастание сложности ПО, создаваемого в различных областях экономики. Современные крупные проекты ИС характеризуют, как правило, следующие особенности:
- • сложность описания (достаточно большое количество функций, процессов, элементов данных и сложные взаимосвязи между ними), требующая тщательного моделирования и анализа данных и процессов;
- • наличие совокупности тесно взаимодействующих компонентов (подсистем), имеющих локальные задачи и цели функционирования (например, традиционных приложений, связанных с обработкой транзакций и решением регламентных задач, и приложений аналитической обработки (поддержки принятия решений), использующих нерегламентированные запросы к данным);
- • отсутствие полных аналогов, ограничивающее возможность использования каких-либо типовых проектных решений и прикладных систем;
- • необходимость интеграции существующих и вновь разрабатываемых приложений;
- • функционирование в неоднородной среде на нескольких аппаратных платформах;
- • разобщенность и разнородность отдельных групп разработчиков по уровню квалификации и сложившимся традициям использования тех или иных инструментальных средств;
- • значительная временная протяженность проекта, обусловленная, с одной стороны, ограниченными возможностями коллектива разработчиков и, с другой стороны, масштабами организации-заказчика и различной степенью готовности отдельных ее подразделений к внедрению ИС.
Как отмечает Фредерик Брукс, руководитель проекта разработки операционной системы OS/360, самым существенным и неотъемлемым свойством программных систем является их сложность. Благодаря уникальности и несхожести своих составных частей программные системы принципиально отличаются от технических систем (например, компьютеров), в которых преобладают повторяющиеся элементы.
Сами компьютеры сложнее, чем большинство продуктов человеческой деятельности. Количество их возможных состояний очень велико, поэтому их так трудно понимать, описывать и тестировать. У программных систем количество возможных состояний на порядок величин превышает количество состояний компьютеров.
Аналогично масштабирование программного объекта — это не просто увеличение в размере тех же самых элементов, это обязательно увеличение числа различных элементов. В большинстве случаев эти элементы взаимодействуют между собой нелинейным образом, и сложность целого также возрастает нелинейно.
Сложность ПО является существенным, а не второстепенным свойством. Поэтому попытки описать программные объекты, абстрагируясь от их сложности, приводят к абстрагированию и от их сущности.
Математика и физика за три столетия достигли больших успехов, создавая упрощенные модели сложных физических явлений, получая из этих моделей свойства и проверяя их опытным путем.
Это удавалось благодаря тому, что сложность, игнорировавшаяся в моделях, не была существенным свойством явлений. Такой подход не работает, когда сложность является сущностью.
Многие проблемы разработки ПО следуют из этой сложности и ее нелинейного роста при увеличении размера.
Сложность является причиной затруднений, возникающих в процессе общения между разработчиками, что ведет к ошибкам в продукте, превышению стоимости разработки, затягиванию выполнения графиков работ.
Сложность вызывает трудности понимания всех возможных состояний программ, что приводит к снижению их надежности. Сложность структуры сдерживает развитие ПО и возможности добавления новых функций.
Уменьшите сложность программного обеспечения шаг за шагом — Русские Блоги
При разработке программного обеспечения мы часто стремимся обеспечить высокую ремонтопригодность программного обеспечения.
Высокая ремонтопригодность означает, что при появлении новых требований систему легко расширять, а при возникновении ошибок — легко находить разработчиков.
Когда мы говорим, что ремонтопригодность системы слишком плохая, это часто означает, что система слишком сложна, что приводит к ошибкам при добавлении новых функций в систему, и ее трудно обнаружить после появления ошибок.
Итак, как определяется сложность программного обеспечения?
Определение, данное Джоном Оустерхаутом, выглядит следующим образом:
Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.
Можно видеть, что сложность программного обеспечения является очень общей концепцией: все, что затрудняет понимание и модификацию программного обеспечения, относится к сложности программного обеспечения. С этой целью Джон Оустерхаут предложил формулу для измерения сложности системы:
C
=
∑
p
c
p
t
p
C = sum_{p}c_{p}t_{p}
C=p∑cptp
В формуле
p
p
pПредставляет модуль в системе,
c
p
c_{p}
cpПредставляет когнитивную нагрузку модуля (Когнитивная нагрузка, то есть степень сложности модуля для понимания),
t
p
t_{p}
tpПредставляет время разработки, потраченное на этот модуль в повседневной разработке.
С точки зрения формулы сложность программного обеспечения накапливается сложностью его различных модулей, иСложность модуля = когнитивная нагрузка на модуль * время разработки модуля, То естьСложность модуля связана не только с самим модулем, но и со временем разработки, потраченным на модуль., Следует отметить, что если модуль очень трудно понять, но он редко участвует в последующем процессе разработки, то его сложность также очень мала.
Что заставляет программное обеспечение быть сложным
Есть много причин для сложности программного обеспечения, но есть не более двух видов:зависимости с участием неясность, Первое сделает модификацию очень трудоемкой и склонной к ошибкам, например, при модификацииМодуль 1Часто это также включаетМодуль 2、Модуль 3、… Изменения: последнее затруднит понимание программного обеспечения, выявление ошибки или даже просто чтение фрагмента кода, отнимает много времени.
Сложность программного обеспечения часто сопровождается следующими симптомами:
Изменить усиление, Когда нужно изменить только одну функцию, но нужно изменить много модулей, мы называем этоМодификация ружья, Обычно это происходит из-за чрезмерной связи между модулямиполагатьсяПеребор.
Например, есть набор веб-страниц, каждая страница является файлом HTML, и каждый HTML имеетзадний планАтрибуты.
Так как различные HTMLзадний планАтрибуты определяются отдельно, поэтому, если вам нужно изменить цвет фона с оранжевого на синий, вам нужно изменить все файлы HTML.
Когнитивная нагрузка, Когда мы говорим, модульзатенитьКогда его трудно понять, он несет чрезмерную когнитивную нагрузку, и в этом случае читатель часто тратит много времени на понимание функции модуля.
Например, предоставьтеcalculateИнтерфейс, он имеет 2intТип ввода и одинintВозвращаемое значение типа.
Судя по сигнатуре функции, вызывающий просто не может знать, что это за функция, и может только вызвать функцию, потратив время на чтение исходного кода для определения функции функции.
int calculate(int val1, int val2);
Неизвестные неизвестные, По сравнению с первыми двумя симптомами,неопределенностьЭто более разрушительно, обычно относится к некоторым моментам, на которые вы должны обратить внимание при разработке требований, но нет способа узнать. Это часто из-за некоторыхзатенитьизполагатьсяВ результате вы почувствуете себя очень некомфортно после разработки требования, смутно почувствуете, что в вашем коде есть проблема, но неясно, в чем проблема.
Как уменьшить сложность программного обеспечения
Скажи нет «тактическому программированию»!
Когда многие программисты выполняют разработку функций или исправляют ошибки, основное внимание часто уделяется тому, как заставить программу работать быстро и быстро.Это типичный метод * Tactical Programming *, который преследуетКраткосрочные выгоды—— Экономьте время разработки.
Самым распространенным проявлением тактического программирования является то, что до написания кода нет модульного дизайна. Тактическое программирование может быть более удобным на ранней стадии системы: если система огромна и связь между модулями становится более тяжелой, добавление или изменение функций и исправление ошибок станут невозможными.
По мере того, как система становилась все более и более сложной, ее в конечном итоге пришлось реконструировать или даже переписать.
Противоположностью тактического программирования являетсяСтратегическое программированиеЧто он преследуетДолгосрочные выгоды-Увеличить ремонтопригодность системы. Недостаточно просто запустить программу.
Вам также необходимо учитывать возможность сопровождения программы, чтобы вы могли быстро реагировать при добавлении или изменении функций или исправлении ошибок.
Поскольку есть еще несколько моментов, на которые стоит обратить внимание,Стратегическое программированиеДля разработки модуля требуется определенное время, но по сравнению сТактическое программированиеПроблемы, вызванные позже, на этот раз полностью стоят того.
Сделайте модуль немного глубже!
Модуль состоит изинтерфейс(Интерфейс) идостигать(Реализация) состоит из двух частей. Если модуль уподоблен прямоугольнику, то интерфейс — это верхний край прямоугольника, а реализация — это область прямоугольника (вы также можете думать о реализации как о функции, предоставляемой модулем).
Когда функции, предоставляемые модулем, определены,Глубокий модуль(Глубокий модуль) характеризуется короткой стороной вершины прямоугольника, общая форма высокая и тонкая, то есть интерфейс относительно простой;Мелкий модуль(Мелкий модуль) характеризуется более длинными сторонами вершины прямоугольника, общая форма короткая и толстая, то есть интерфейс более сложный.
Пользователи модуля часто видят только интерфейс: чем глубже модуль, тем меньше информации он предоставляет вызывающему, и тем меньше связь между вызывающим и модулем. Поэтому ставимМодуль предназначен для «глубже»Помогите уменьшить сложность системы.
Итак, как может быть разработан глубокий модуль?
- Простой интерфейс более важен, чем простая реализация. Более простой интерфейс означает, что модуль проще в использовании и удобнее для вызывающего абонента. иПростая реализация + сложный интерфейсЭта форма, с одной стороны, влияет на простоту использования интерфейса, с другой стороны, она углубляет связь между вызывающим абонентом и модулем. Поэтому при проектировании модуля лучше всего соблюдать «Оставь простоту другим, оставь сложность себе»правила.
Исключения также являются частью интерфейса.В процессе кодирования, мы должны положить конец явлению выброса исключений без обработки, что только увеличит сложность системы.
- При разработке интерфейса у вас часто есть два варианта: (1) дизайн как выделенный интерфейс (2) дизайн как универсальный интерфейс. Первый вариант более удобен для реализации и может полностью удовлетворить текущие потребности, но масштабируемость низкая и относится кТактическое программированиеПоследнее требует времени для абстрагирования системы, но масштабируемость высока и принадлежитСтратегическое программирование, Общий интерфейс означает, что интерфейс применяется более чем к одному сценарию, и типичным является «Один интерфейс, несколько реализаций «Форма. Некоторые программисты могут опровергнуть, что GM означает чрезмерное проектирование, когда невозможно предсказать будущие изменения. Чрезмерное обобщение действительно является чрезмерным дизайном, но умеренная абстракция интерфейса, напротив, не может сделать систему более многоуровневой и обслуживаемой.
- При разработке модуля мы также должны научиться различать, какая информация важна для звонящего, а какая информация не важна.Скрыть деталиОтносится кПредоставляйте звонящему только важную информацию и скрывайте неважные детали, Одна из скрытых деталей упрощает интерфейс модуля, а другая облегчает обслуживание системы.
Как судить, важны ли детали для звонящего? Вот несколько примеров:
- Для JavaMapинтерфейс,Важные детали:MapКаждый элемент всостоит из;Неважные детали:MapНижний слой — как хранить эти элементы и как обеспечить безопасность потоков.
- Дляreadфункция,Важные детали: Какой файл читать и сколько байтов читать из каждой операции чтения;Неважные детали: Как перейти в режим ядра, как читать данные с жесткого диска и т. Д.
- Для многопоточных приложений,Важные детали: Как создать тему;Неважные детали: Как многоядерный процессор планирует поток.
Делай слоистый дизайн!
Хорошо спроектированная программная архитектура имеет одну характеристику, то есть уровни прозрачны, каждый уровень предоставляет разные абстракции, а зависимости между уровнями очевидны.
Будь то классическая веб-трехуровневая архитектура, четырехуровневая архитектура и шестиугольная архитектура, поддерживаемая DDD, или так называемая чистая архитектура, существует четкое чувство иерархии.
При выполнении многоуровневого проектирования следует отметить, что каждый слой должен обеспечивать разные абстракции и стараться избегать большого количестваPass-Through Mehod, Например, в четырехслойной архитектуре DDDУровень доменаПредоставляет абстракцию доменной бизнес-логики,Прикладной уровеньПредоставляет абстракцию вариантов использования системы,Интерфейсный уровеньПредоставляет абстракцию интерфейса доступа к системе,Уровень инфраструктурыЭто обеспечивает абстракцию основных услуг, таких как доступ к базе данных.
Так называемыйPass-Through MehodОтносится к темНепосредственно вызывайте другие функции в теле функции, делая при этом очень мало вещей«, сигнатура функции обычно аналогична сигнатуре функции, вызываемой ею. Модули, в которых расположен Pass-Through Mehod, обычно являются поверхностными модулями, которые добавляют ненужные уровни и вызовы функций в систему, что делает систему более сложной.
Научитесь писать комментарии к коду!
ПометкиЭто очень рентабельный метод в процессе разработки программного обеспечения, который занимает всего 20% времени, чтобы получить 80% стоимости.
это можетУлучшить читаемость неясного кода; Может игратьСкрыть детали сложного кодаТакая роль, как комментарии к интерфейсу, может помочь разработчикам быстро понять функцию и использование интерфейса, не читая код, при правильном написании онУлучшить дизайн системы。
Конкретно, как написать код , ссылки«Научите писать хорошие комментарии к коду»Одна статья
подводить итоги
Сложность программного обеспеченияЭто то, с чем наши программисты должны сталкиваться в повседневной разработке, научитьсяВыясните, в чем заключается сложность программного обеспечения, найдите причину сложности программного обеспечения и используйте различные методы для преодоления сложности программного обеспечения.«Это необходимая способность. Есть хорошая поговорка:« Качество кода определяет качество жизни ». Когда вы уменьшаете сложность программного обеспечения, количество ошибок уменьшается, а обслуживаемость системы выше, и, естественно, это принесет Здесь приходит лучшее качество жизни.
Модульная конструкцияЯвляется ли самый эффективный способ уменьшить сложность программного обеспечения, научиться использовать «Стратегическое программированиеИ придерживайтесь этого. Мы часто выступаем за «сделать все сразу», но это не применимо к разработке модулей, и почти никто не может разработать модуль в идеальном виде в первый раз.
Вторичный дизайнЭто очень эффективный метод. Вместо того, чтобы тратить много времени на рефакторинг или переписывание после того, как система повреждена, лучше потратить некоторое время на второй дизайн после завершения первого проекта модуля.
Задайте себе вопрос:Есть ли более простой интерфейс?Есть ли более общий дизайн?Есть ли более лаконичная и эффективная реализация??
«Рим не был построен за один день», так что это то же самое, чтобы уменьшить сложность программного обеспечения.