Пиццикато Алексея Лота

Полезные высказывания из книги "Надежный код" Бруно

Более высокий уровень абстрагирования позволяет больше времени уделять важным элементам любого проекта.

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

Разработка ПО - это не инженерия.

Настройка производительности и безопасности не должна откладываться на конец проекта.

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

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

Нереалистично тестировать ПО в конце цикла разработки.

agilealliance.org

agilemanifesto.org/principles.html

Сейчас используются Scrum (наиболее широко применяется в Microsoft), XP, TDD.

blogs.msdn.com/e7

Шаги разработки:
сбор сведений от заказчика, совместное обсуждение требований, детальное тестирование, описание параметров компонентов и общей архитектуры системы;
процедуры контроля качества кода - стандарты, совместная разработка, оптимизация, модульное тестирование;
обширное тестирование системы и сборки.

Методы контроля качества применять как можно раньше.

Чем больше кода написано, тем сложнее его тестировать.

Стремиться и сосредоточиться на качестве, надежности, безопасности.

Применять итеративную разработку с итерациями не более 6-8 недель, полностью завершать один компонент перед началом работы, разделять работы на несколько автономных групп, достаточно часто обмениваться информацией о состоянии проекта.

Использовать типичные методологии развертывания, оборудования и инструментов, планировать процессы, стремясь к предсказуемости и повторяемости процедур.

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

Отказаться от модели водопада.

Создавать проверяемые модули.

Ежедневно запускать сборку и запускать автоматические BVT-тесты.

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

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

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

Производительность с самого начала часть любого проекта.

Факторы масштабирования - часть дизайна приложения.

Безопасность закладывается в дизайне приложения.

Тестеры гарантируют полное покрытие кода тестами.

Учиться эффективно управлять памятью.

Использовать безопасное программирование.

Сердцем программирования являются этапы анализа требований и проектирования.

Цикл разработки ПО:
анализ требований;
проектирование;
спецификации;
программирование;
тестирование;
развертывание;
обслуживание.

Написанию кода обязательно должен предшествовать этап проектирования.

Дизайн приложения в идеале не зависит от особенностей реализации.

В ООП 40-50% времени разработчик тратит на проектирование кода.

Число написанных строк кода - неверный показатель труда.

Секрет успеха заключается в неизменности цели.

На устранение проблемы на этапе тестирования требуется в 4 раза больше времени, чем на этапе проектирования.

C# - прямой потомок C++.

Существительные из предметной области могут стать объектами, глаголы и глагольные группы - поведением.

Проектирование должно управлять реализацией, но не наоборот.

Из списка объектов предметной области удаляются не взаимодействующие с другими.

Использовать UML для моделирования в проектировании.

Типы UML схем см. в книге.

Изоляция классов друг от друга - один из принципов ООП.

В конструкторе классов VS изменения в схеме классов немедленно отражаются в коде и наоборот.

Один из способов повысить гибкость кода - писать его как можно меньше.

Метаданные хранить в xml-файлах конфигурации в корневом каталоге приложения (не использовать ini и реестр).

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

Настройки, применимые ко всем приложениям на данном компьютере, хранятся в machine.config.

Храните в метаданных настройки приложения.

Храните в метаданных данные приложения.

Управляйте поведением приложения при помощи метаданных.

Все компоненты должны при возможности управляться конфигурацией.

Изменение файлов конфигурации не требует регрессионного тестирования.

Использовать сжатие данных, передаваемых по сети.

Не использовать множество маленьких статических избражений.

Стандарт HTTP/1.1 предполагает одновременную загрузку браузером только двух ресурсов с одного имени хоста (новые браузеры игнорируют это требование).

Не злоупотреблять перенаправлением страниц.

В веб-приложении не стоит использовать много уникальных имен хостов, чтобы избежать излишних запросов DNS.

Максимально сокращать объем данных, передаваемых по сети, для оптимизации производительности.

Добивайтесь максимально эффективного использования пропускной способности сети.

Свести число HTTP-запросов к минимуму.

Используйте сжатие HTTP.

Минимизируйте объем кодов JavaScript и CSS (Yui Compressor, JsMin).

Уменьшайте палитру изображений.

Настраивайте сроки действия даных (Expires, Cache-Control, ETag).

Прочитать High Performance WebSites (Souders), mnot.net/cache_docs.

Увеличение числа параллельных TCP-портов позволит параллельно загружать больше содержимого.

HTTP/1.0 требовал создания нового соединения для каждого HTTP-запроса. Пакеты keep-alive позволяют браузерам отправлять несколько HTTP-запросов с одного соединения, что сокращает сетевой траффик за счет уменьшения числа открываемых и закрываемых соединений (заголовок Connection: keep-alive).

Запросы к DNS могут занимать до 120 мс.

Уменьшение числа обращений к DNS (рекомендуется <=4-6 уникальных имен хостов) улучшит время ответа страницы, но может негативно повлиять на параллельную загрузку содержимого.

Если у приложения мало ресурсов, в общем случае лучше обойтись одним именем хоста.

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

Используйте сети доставки содержимого (CDN).

Внедряйте спрайты CSS - вместо загрузки одного изображения загружайте несколько параллельно (для мелких значков используйте один спрайт).

В общем случае внедрение JavaScript и CSS в страницу ускоряет прорисовку.

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

Размещайте CSS в начале страницы (если файлы CSS расположены вне блока HEAD, браузер заблокирует прогрессивную прорисовку).

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

Программа совершенствования производительности:
определить сценарии использования и приоритеты;
проанализировать разработки конкурентов;
сформулировать задачи производительности;
внедрять эффективные методики;
измерять и тестировать.

Устанавливайте ограничения производительности для ключевых сценариев (например, количество запросов get).

Непрерывно анализируйте и тестируйте производительность.

Экспериментируйте и разбирайтесь в поведении пользователя (измененная версия приложения предоставляется небольшой группе пользователей).

Упреждающе загружать страницы и сценарии, о которых известно, что пользователь будет их посещать.

Выясните, какие факторы влияют на производительность работы конечного пользователя.

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

Приложение считается масштабируемым (scalable), если его производительность возрастает по мере расширения аппаратной базы.

Необходима возможность наращивать мощность приложения за счет инфраструктуры - новых серверов, накопителей данных и сетевого оборудования (работа пользователей при этом не должна зависеть от объема нагрузки).

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

Чем больше оборудования и чем шире развернуто ПО, тем сложнее им управлять и обслуживать его, поэтому очень важно, чтобы разработчики приложения проектировали его таким образом, чтобы его просто было настраивать, развертывать и контролировать.

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

Веб-приложение можно на любом языке спроектировать так, что оно будет масштабируемым.

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

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

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

Две основные стратегии масштабирования приложений при расширении аппаратной базы: вертикальное (scaling up) и горизонтальное (scaling out).

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

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

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

При масштабировании уровня БД веб-приложения к вашим услугам вертикальное и горизонтальное масштабирования.

В MSSQL масштабирование выполняется созданием федерации и секционированием.

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

Выбирайте дизайн, пригодный для горизонтального масштабирования.

Используйте устройства для балансировки нагрузки (серверы можно добавлять и удалять в процессе работы).

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

При сбое в ключевом компоненте приложение должно продолжать работу в сокращенном объеме (приложение должно быть отказоустойчивым).

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

На разработчике лежит ответственность за своевременное информирование руководства о важности создания отказоустойчивой инфраструктуры и о возможных вариантах её создания.

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

Сужение функциональности лучше, чем сообщение об ошибке.

Приложения должны быть просты в эксплуатации (приложение должно обладать управляемостью и сопровождаемостью).

Данные инструментирования может собирать отдельная служба.

Ведите активный мониторинг приложения.

Выделите ключевые метрики и цели мониторинга.

Планируйте стратегию роста и стратегию реакции на сбои.

Масштабирование "по ролям": отдельные функциональные области масштабируются независимо друг от друга.

В каждом компоненте приложения предусматривайте аварийный выход.

Автоматизируйте основные задачи управления.

Составляйте план восстановления системы.

Постоянно оценивайте загруженность инфратсруктуры и планируйте её развитие.

"Writing secure code" Ховард

"Threat modeling" Свидерски

Используйте механизмы проверки подлинности и авторизации .NET (Windows Identity, Windows Principle, Generic Identity, Generic Principle).

Шифруйте конфиденциальные данные.

Считайте внешние приложения небезопасными по умолчанию.

Реализуйте безопасную обработку отказов.

Реализуйте безопасную обработку ошибок и исключений.

Приложения следует проектировать так, чтобы они могли выполняться с наименьшими возможными полномочиями (Anti-Cross Site Scripting Library).

Включайте в конфигурацию по умолчанию только необходимые компоненты.

По умолчанию приложение должно работать с ограниченными полномочиями.

Организуйте процесс сопровождения приложения и устранения ошибок (устранение сбоев и сбор отзывов).

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

Соблюдайте требования законодательства.

Организуйте диалог с пользователями по вопросам безопасности.

Разработайте план реагирования на проблемы с безопасностью.

".NET Security Programming" Маршал

Элементы политики безопсасности времени выполнения:

В .NET поддерживаются пользовательские разрешения и наборы разрешений.

Свидетельство источника предоставляется хостом, запрашивающим выполнение сборки.

Кодовая группа связывает свидетельство с набором разрешений.

Уровни политики определяют границы предоставляемых разрешений.

.NET Framework Configuration Tool
Code Access Security Policy Tool
Permissions View Tool
Build Verification Testing
Final Security Review
Intrusion Detection Systems
FxCop

В динамическом коде память выделяется динамическим объектам по требованию при помощи оператора new.

Модель управления памятью в .NET опирается на два основных допущения: большие объекты живут долго и друг с другом чаще всего взаимодействуют объекты близкого размера.

Управляемая куча делится на поколения и кучу больших объектов. Поколения 0, 1 и 2 используются для группировки объектов по размеру и времени жизни. 2 - самое старое поколение с самыми большими объектами. 0,1 - эфемерные поколения. Большими считаются объекты более 85000 байт. Большие объекты хранятся в куче больших объектов. Задача сборщика мусора - освободить память. Сборку мусора запускают события:
- После очередного выделения памяти в случае его успешного завершения превышен порог для поколения 0. Новые объекты всегда помещаются в поколение 0. В поколении 0 всегда находятся самые молодые объекты.
- В куче больших объектов не хватает памяти.
- Вызван метод GC.Collect.
Сборщик мусора собирает сначала 0 поколение, затем 1 и 2. Если обрабатывается какое-либо поколение, обязательо обрабатывается и младшее. Для объектов, оставшихся после сборки, сборщик повышает поколение. При сборке мусора объекты в обрабатываемых поколениях признаются недоступными. Дерево памяти при сборке мусора перестраивается. Объекты, не попавшие в дерево, считаются недостижимыми и готовыми к сборке. Сборщик мусора сжимает доступные объекты в управляемой куче. Использовать метод GC.Collect не рекомендуется, так как он способен помешать работе сборщика мусора в штатном режиме. Управляемый класс - это интерфейс между управляемым приложением и неуправляемыми ресурсами. Сборщик мусора будет искать в памяти только управляемый интерфейсный класс, не учитывая память для неуправляемого ресурса (GC.AddMemoryPressure и GC.RemoveMemmoryPressure). Вызовите метод GC.AddMemoryPressure в конструкторе управляемого класса, чтобы применить нагрузку, равную памяти неуправляемого ресурса. В методе Finalize или Dispose вызовите GC.RemoveMemoryPressure.

Сборщик обрабатывает кучу больших объектов (LOH) при полной сборке мусора. Память в куче больших объектов не сжимается. Для крупных объектов лучше создавать буфер - это позволяет выделять для больших объектов смежные блоки памяти (вы экономите память, минимизируете фрагментацию и сокращаете количество полных (наиболее ресурсоемких) сборок мусора). При помощи Finalize() не стоит очищать управляемые объекты. Уничтожение объектов с дескриптором требует 2 цикла сборки мусора. В отличие от Finalize(), метод IDisposable.Dispose детерминирован, вызывается вручную и выполняется без задержки. В методе Dispose можно ссылаться как на упрвляемые, так и на неуправляемые ресурсы. Библиотека .NET автоматически вызывает Dispose, если реализован IDisposable.

Упаковки приводят к дополнительным сборкам мусора.

Используйте метода Finalize только при крайней необходимости.

Для отладки управляемой кучи использовать CLR Profiler.

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

Всегда задавайте максимальный уровень предупреждений компилятора.

В новых приложениях обрабатывайте предупреждения как ошибки.

Выполняйте совместный анализ кода при добавлении кода в систему управления версиями и при развертывании продукта. Тестирование разделяется на верификацию и валидацию. Лучше рассматривать покрытие кода как косвенное подтверждение надежности приложения. Покрытие функций - выполняется ли конкретная функция и если да, то как часто. Покрытие операторов - выполнялась ли строка кода и если да, то как часто. Покрытие путей - выполнялась ли определенная ветвь кода. Уровень покрытия кода должен быть определен прежде, чем начнется программирование, согласован и доведен до всех участников команды. Если исходный код не удовлетворяет заданному стандарту, его нельзя передавать в систему управления версиями. Комментируйте все и всегда. Комментарии должны почти надоедать. Код не должен содержать трюков. /// - комментарий документации.

В начале функции проверяйте корректость переданных в нее параметров.

По возможности решайте проблемы с помощью обработки ошибок, а не исключений.

По возможности функции должны возвращать значения, отличные от void.

Всегда проверяйте значение, возвращаемое функцией.

Не пишите небезопасный код.

Не перехватывайте "избранные" исключения.

Избегайте определения или использования объектных типов (boxing и downcasting снижают производительность).

Всегда используйте общие (generic) коллекции вместо стандартных.

По возможности вместо коллекций используйте массивы.

Не используйте собственные API приложения для реализации функциональности, имеющейся в .NET Framework Class Library (FCL).

Из всех операторов циклов отдавайте предпочтение циклу for (дисциплинирует).

Присваивайте неиспользуемым объектам null.

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

Разбивайте сложное выражение на простые подвыражения.

Придерживайтесь рекомендаций CLS.

Проверяйте на overflow и underflow с помощью MinValue, MaxValue.

Избегайте ненужной рекурсии.

microsoft.com/patterns
Шаблоны проектирования представляют собой известные и проверенные решения типовых задач программирования. В плане разработки выделять время на отладку. Разработчики оперируют разрабатываемой версией, а клиенты получают рабочую версию. Основная причина возникновения проблем с ПО - недостаток образования.
watin.sourceforge.net

Многопотоковые приложения тестировать на разном количестве ядер.

ADPlus

Отладочные версии приложений не оптимизируются.

В отладочной версии веб-приложения не истекает срок действия запросов ASP.NET.

Для отладочных версий веб-приложений не предусмотрена пакетная компиляция.

Большое количество сборок приводит к фрагментации виртуальной памяти.

В отладочной версии веб-приложения клиентские библиотеки кода и статические изображения из файла webresources.axd не кешируются.

ILDASM/ILASM, Reflector, Son of Strike (SOS), Windbg, GFlags.

Трассировка=инструментирование.

Первичное исключение не выбрасывается, а передается на обработку приложению, в отличие от вторичного.

Перехват необрабатываемых ошибок на уровне страницы более приоритетен, чем их перехват на уровне приложения.