5 способов написания кода без багов

Можно ли писать код на 100% без багов?
Летом 2015 года группа хакеров попыталась взять под контроль беспилотный засекреченный военный вертолет, известный как "Little Bird".
У хакеров была фора, и за короткое время они взломали одну из частей компьютерной системы беспилотника. Дальше им нужно было влезть в бортовой компьютер Little Bird, и дрон был бы в их руках. Но им это не удалось, и причиной тому стал новый вид механизма безопасности, внедренный DARPA (Агентством перспективных оборонных исследовательских проектов) под названием "формальная или математическая верификация".
Согласно этому механизму критические части компьютерной системы Little Bird были сделаны неподступными с помощью существующих технологий, а их код был сделан защищенным на 100% как математическое доказательство.
В отличие от большинства компьютерных программ, которые тестируются после написания, формально проверенное ПО читается как математическое доказательство. Каждое утверждение логически вытекает из предыдущего. Вся программа может быть проверена с такой же уверенностью, с какой математики доказывают теоремы.
Как позже справедливо заметила Кэтлин Фишер, руководитель программы проекта высоконадежных кибернетических военных систем (HACMS):
Они не смогли взломать и каким-либо образом нарушить ход операции. Этот результат заставил весь DARPA встать и сказать: "Боже мой, мы действительно можем использовать эту технологию в системах, которые для нас многое значат".
Единственный способ иметь код 100%-но свободный от ошибок - это доказать его математически. Очень немногие программы в мире доказываются математически просто потому, что это слишком дорого. Большинство из нас работает над проектами, которые не могут оправдать затраты на математическое доказательство, и поэтому нам приходится полагаться на наш небольшой набор трюков, чтобы поддерживать количество ошибок как можно ниже.
Тем не менее, мы все еще можем писать код без ошибок, и под кодом без ошибок я понимаю ПО с приемлемым качеством, разработанное в рамках заданной стоимости и времени. Мы стремимся минимизировать количество ошибок, делая более дешевые ошибки, чтобы избежать более дорогих. Таким образом, мы можем достичь разумного уровня совершенства, который оправдывает инвестиции в проект.
Мы можем достичь этого пятью способами:
- Не игнорируйте предупреждения.
- Автоматизируйте тесты.
- Держите под контролем входные значения.
- Уменьшите ветвление в коде.
- Прислушивайтесь к пользователю.
1. Не игнорируйте предупреждения

Когда я был новичком в программировании, я создавал приложения, и в большинстве случаев предупреждения извергались бесконечным потоком. И когда я спрашивал об этих предупреждениях у коллег-программистов, они говорили мне, чтобы я не обращал на них внимания, так как предупреждения безвредны.
Но правда ли это? Действительно ли они безвредны? Я понял это на собственном опыте - я понял, что устранение предупреждений всегда приводит к уменьшению количества ошибок и менее хрупкому коду. Предупреждения - это просто ошибки, ожидающие своего появления, поэтому игнорировать их опасно по следующим причинам:
- Предупреждения - это поведение по умолчанию вашего компилятора, которое может не соответствовать правильному сценарию его применения.
- По мере накопления предупреждений ошибки становится все труднее исправлять и предотвращать, поскольку код вскоре может выйти из-под контроля.
- Большое количество предупреждений означает отсутствие командной дисциплины. Это показывает, что стандарты кодирования небрежны.
- У команды может выработаться привычка игнорировать все предупреждения, что также может привести к подавлению вредных предупреждений.
Хотя на исправление предупреждения требуется больше времени, чем на его игнорирование, это время потрачено с пользой и в будущем приведет к созданию более чистого и качественного кода. Чтобы выявить потенциальные ошибки, установите уровень предупреждений в компиляторе на максимум. Не терпите предупреждения. Устраняйте их.
2. Автоматизируйте тесты

Автоматизация тестирования повышает общую эффективность и обеспечивает надежное качество программного обеспечения. Существуют специальные инструменты, которые позволяют эффективно автоматизировать тестовые сценарии и сравнивать фактические и ожидаемые результаты.
К преимуществам автоматизации тестирования можно отнести:
- Более быстрая обратная связь: Это улучшает коммуникацию между программистами, проектировщиками и владельцами проекта и позволяет немедленно устранять потенциальные неполадки.
- Ускоренные результаты: Тестирование можно проводить многократно, каждый раз получая более быстрые результаты с меньшими затратами сил и времени.
- Повышение эффективности тестирования: Автоматизированные тесты в конечном итоге занимают значительно меньше времени. Их можно выполнять практически без присмотра, оставляя проверку результатов на конец процесса.
- Более высокий охват тестов: Автоматизированное тестирование приводит к тому, что в проекте выполняется больше тестов. Это приводит к более высокому покрытию, чем при ручном тестировании.
- Более раннее обнаружение дефектов: Раннее обнаружение дефектов помогает увеличить общую скорость разработки, обеспечивая при этом правильную функциональность во всех областях. Чем раньше выявлен дефект, тем экономичнее его устранение.
Помните, что наличие надежной батареи автоматизированных тестов - это отличный способ снизить количество ошибок и избежать появления новых ошибок во время рефакторинга кода. Первоначальные инвестиции могут быть высокими, но затраты оправдывают усилия, сэкономленные на последующих этапах проекта.
3. Держите под контролем входные значения

Все мы знаем извечную пословицу "мусор внутрь, мусор наружу". Поэтому, если ваши входные данные являются мусором, вы обязательно получите ошибки.
Всем программам необходимо получать данные из внешних источников, которые могут быть ненадежными или сомнительными. Источником может быть пользовательский интерфейс, файл, внешняя система или база данных. Например, вы ожидаете ввода номера кредитной карты, а кто-то вводит SSN (номер социального страхования). Вы ожидаете, что будет введен почтовый индекс США, а кто-то вводит почтовый индекс в индийском формате. Диапазон ошибок бесконечен.
Чтобы сделать вашу программу надежной, вы должны проверять каждый ввод в вашу программу. Я знаю, что валидация увеличивает размер программы и снижает производительность. Следовательно, очень важно тщательно разграничить "минимум" валидации, который можно допустить, и валидацию, которая абсолютно "не подлежит обсуждению".
При валидации вы должны убедиться, что все данные проверяются только в одном месте, где ваша программа получает входные данные. Поместив всю логику валидации в одно место, вы сможете организованно проверять недопустимые данные, и не будет необходимости перепроверять их в других местах.
Помните, что управление входными данными программы - это очевидная, но часто упускаемая из виду техника борьбы с ошибками. Но она крайне важна, и ее нельзя недооценивать.
4. Уменьшите ветвление в коде

Недавно я посмотрел доклад Мишко Хевери, в котором он предложил аудитории интересную задачу. Он хотел, чтобы они написали код без блоков if-else или switch.
Возможно ли это вообще? Построение условной логики - это сердце любой программы, да и что может дать такое упражнение?
Позже Хевери объясняет, что тестирование каждой комбинации становится невозможным по мере увеличения количества условных операторов, и ошибки начинают просачиваться из немыслимых сценариев. Кроме того, слишком много условной логики делает программу более сложной для понимания и исправления.
Суть Хевери в том, что код без операторов if (или switch) является:
- Легче читать и понимать;
- Легче тестировать;
- Легче поддерживать, расширять и т.д.
Хотя он согласен с тем, что полное исключение условной логики неизбежно, мы можем уменьшить ее количество, используя следующие способы,
- Хорошее ОО-проектирование может устранить необходимость в явном кодировании ветвления.
- Использование
assert
для проверки достоверности. - Использование контекста и полиморфизма.
- Устранение "крушениий поезда". "Крушение поезда" - это несколько вызовов методов (называемых цепочкой), в которых код вызывает метод возвращаемого значения предыдущего метода. Такая глубоко вложенная модель противоречит закону Деметры - рекомендации по проектированию, направленной на обеспечение слабого сцепления структур данных, которые не являются тесно связанными и поэтому не должны быть сцеплены.
Помните, что меньше - значит больше. Чем больше кода вы пишете в продакшн, тем больше вам придется его поддерживать, и тем более скептически вы должны относиться к тому, насколько он действительно читабелен и не содержит ошибок.
5. Прислушивайтесь к пользователю

Да. Под пользователями я подразумеваю реальных пользователей, которые будут использовать приложение, а не тестировщиков, проверяющих ваш код.
Вы можете поспорить здесь". Тестер проверил мой код. Почему я должен беспокоиться?" Неверно. Вы несете ответственность за предоставление корректного кода, который удовлетворит пользователей, и вам необходимо сделать статистические выводы из их отзывов, чтобы оценить стабильность или нестабильность вашего кода.
Тем не менее, большинство ошибок возникает либо в измененном, либо в новом коде. Обнаружение ошибки в уже стабильном коде, который хорошо работает в производстве, происходит нечасто. Но иногда это все же случается.
А как вы узнаете, что какой-то код хорошо работает в течение длительного времени в производстве? Просто прислушиваясь к мнению пользователей. Если они не сообщали о проблемах в некоторых функциях в течение длительного времени, вы можете быть уверены, что основной код стабилен.
Помните, что реальные пользователи будут жаловаться, когда найдут ошибку, и молчать, когда посчитают, что продукт работает нормально. Это может стать вашей лакмусовой бумажкой как разработчика, чтобы улучшать себя после каждого такого отзыва. Некоторые из наиболее ценных отзывов также могут быть использованы для решения следующих проблем:
- Функциональные проблемы в коде;
- Отсутствующая валидация и крайние случаи;
- Использование API и шаблоны проектирования.
И так далее.
Заключительные размышления

Ничто в этом мире не совершенно, в том числе и программное обеспечение. Оно требует тщательного тестирования перед запуском и постоянного обновления после запуска, чтобы оставаться актуальным и обеспечивать хороший пользовательский опыт.
Даже после выпуска программного обеспечения вы не можете контролировать среду его выполнения, поскольку существует множество устройств, на которых оно может работать, и сбои могут произойти где угодно и когда угодно. Не теряйте голову из-за этого.
Лучше всего стремиться к превосходству, а не к идеалу, и понимать, что вы сможете добиться разработки без ошибок, только если построите космический шаттл. Превосходство фокусирует ваше внимание на том, что правильно и что работает, а не на том, что не работает. Превосходствобезгранично, прогрессивно и всегда вознаграждается.
Как справедливо заметил Рик Питино:
"Превосходство - это неограниченная способность улучшать качество того, что вы можете предложить".
Источники
- Writing Solid Code: Development Philosophies for Writing Bug-Free Programs-Steve Maguire
- Test-Driven Development with C++: A simple guide to writing bug-free Agile code-Abdul Wahid Tanner
- Clean Code: A Handbook of Agile Software Craftsmanship-Robert C. Martin
- Timeless Laws of Software Development-Jerry Fitzpatrick
- Complete Guide to Test Automation: Techniques, Practices, and Patterns for Building and Maintaining Effective Software Projects-Arnon Axelrod
Данное содержание является точным и достоверным в меру знаний автора и не заменяет официальную и индивидуальную консультацию квалифицированного специалиста.
Материал подготовлен с ❤️ редакцией Кухни IT.