Программная ошибка, привычно называемая «багом», — это дефект в программе или системе, из‑за которого она ведёт себя неправильно: выдаёт неверные данные, зависает, аварийно закрывается или проявляет другие нежелательные симптомы. Большинство таких ошибок появляется из-за огрехов при проектировании или написании кода, но иногда виноваты инструменты разработки, например компиляторы, которые генерируют некорректный исполняемый файл.
Термин «программная ошибка» обычно применяют к дефектам, проявляющимся при выполнении кода; это отличает их от концептуальных ошибок на уровне архитектуры или от синтаксических ошибок, мешающих компиляции. Отчёт о найденной проблеме называют отчётом об ошибке; если ошибка вызвала аварийное завершение работы, документ обычно именуют крэш‑репортом.
Этимология термина баг
Слово bug в значении «неуловимая техническая неисправность» использовали ещё до компьютеров — сотрудники телеграфа и телефонных сетей так называли неполадки в электронике. В 1878 году Томас Эдисон писал о «жучках», которые появляются после первой идеи и мешают довести изобретение до коммерческого успеха.
Самый известный анекдот связан с 9 сентября 1947 года: Грейс Хоппер работала на Harvard Mark II и обнаружила, что причина сбоя — застрявший между контактами реле мотылёк. Насекомое вклеили в технический дневник с пометкой «первый реальный случай обнаружения жучка», и слово «bug» прочно вошло в лексикон инженеров.
Значение и классификация ошибок программного обеспечения
Разные виды ошибок требуют разных подходов к поиску и исправлению, поэтому их удобно классифицировать по нескольким признакам.
Классификация по этапу выявления
По тому, на каком этапе проявляется дефект, ошибки делят на три группы:
- синтаксические ошибки;
- предупреждения компилятора;
- ошибки времени выполнения или семантические ошибки.
Синтаксические ошибки нарушают правила языка и блокируют компиляцию. Предупреждения указывают на потенциальные проблемы, которые не мешают сборке, но могут привести к багам. Семантические ошибки проявляются только при исполнении — это логические огрехи в алгоритмах или неправильная работа с памятью.
Классификация по степени важности
По влиянию на работу системы ошибки обычно ранжируют так:
- блокирующие (Blockers);
- критические (Critical / Showstoppers);
- серьёзные (Major);
- незначительные (Minor);
- косметические (Cosmetic).
Блокирующие и критические баги делают продукт непригодным для использования; серьёзные снижают удобство, а незначительные и косметические чаще касаются мелких неудобств и внешнего вида.
Классификация по времени появления
По частоте проявления ошибки делятся на три типа:
- постоянные;
- плавающие (Intermittent);
- зависящие от окружения.
Постоянные воспроизводятся стабильно, плавающие — лишь иногда, а окружные проявляются лишь в специфических конфигурациях пользователя или оборудования.
Классификация по месту и направлению
По области кода или сигналам, которые они затрагивают, ошибки бывают такими:
- ошибки пользовательского интерфейса;
- ошибки в системах обработки исключений;
- ошибки, связанные с граничными условиями;
- ошибки вычислений;
- ошибки управления потоками выполнения;
- ошибки обработки или интерпретации данных;
- ошибки состояния гонки (race conditions);
- ошибки при повышении нагрузки на систему;
- ошибки контроля версий и идентификаторов;
- ошибки тестирования.
Некоторые дефекты остаются незамеченными годами и проявляются только при редких условиях — примером служит «Проблема 2038 года», связанная с переполнением счётчика времени в UNIX‑подобных системах. Ошибки также часто открывают путь для атак, включая несанкционированный доступ и DoS‑атаки.
Разновидности специфических ошибок
По характеру нестабильности ошибки традиционно называют так:
- гейзенбаг (Heisenbug);
- борбаг (Bohr bug);
- мандельбаг (Mandelbug);
- шрёдинбаг (Schroedinbug);
- гинденбаг (Hindenbug);
- багсон Хиггса (Higgs‑bugson).
Гейзенбаг меняет поведение при попытке отладки и трудно локализуется. Борбаг стабилен и воспроизводим. Мандельбаг проявляет хаотичное, трудно предсказуемое поведение и порой указывает на ошибки в архитектуре. Шрёдинбаг не даёт о себе знать, пока кто‑то не обнаружит дефект в коде или не воспользуется системой нестандартно. Гинденбаг приводит к катастрофическим последствиям, а багсон Хиггса предсказывается по косвенным данным и почти не воспроизводится в тестах.
Поиск и исправление ошибок
Процесс нахождения и устранения багов называется отладкой. Для этого применяют отладчики, которые позволяют шагать по коду, смотреть значения переменных и ставить точки останова. Примеры популярных инструментов — WinDbg для Windows и GDB для UNIX‑подобных систем.
Отладка требует системного подхода: изоляция проблем (например, бинарный поиск по участкам кода) и логирование поведения программы помогают быстрее локализовать источник сбоя.
Отчёты об ошибках
Часть дефектов исправляют ещё на стадии сборки и тестирования, но некоторые попадают в релизы и обнаруживаются у пользователей. Для сбора сведений о таких случаях используют автоматические механизмы отчётов об ошибках:
Эти системы собирают данные о состоянии приложения в момент сбоя и отправляют их разработчикам. Пример встроенного решения — утилита Dr. Watson в системах Windows. Существуют также сторонние библиотеки — Breakpad и CrashRpt — которые помогают интегрировать сбор крэш‑репортов в приложения.
Последствия программных ошибок
Ошибки в коде могут приводить к самым разным последствиям — от мелких неудобств до серьёзных экономических и человеческих потерь. Конкретные примеры:
- авария ракеты‑носителя «Ариан‑5» 4 июня 1996 года;
- ошибки в медицинском ускорителе Therac‑25 в 1980‑х годах;
- финансовые потери крупной организации в августе 2012 года.
В случае «Ариан‑5» переполнение при преобразовании 64‑битного значения в 16‑битное привело к сбою системы наведения и уничтожению ракеты с четырьмя спутниками на борту. В инцидентах с Therac‑25 программные ошибки вызвали передозировки облучения у пациентов и стали трагическим уроком о необходимости верификации критичных систем. В 2012 году из‑за ошибки в алгоритме высокочастотной торговли организация потеряла 440 миллионов долларов за 45 минут.
Предотвращение ошибок и повышение качества кода
Чтобы снизить количество дефектов, используют набор практик и инструментов, охватывающих весь цикл разработки:
- тщательное проектирование и архитектура;
- стандарты кодирования и ревью кода;
- автоматизированное тестирование;
- статический и динамический анализ кода;
- формальная верификация для критичных систем;
- управление конфигурацией и версиями.
Чёткие требования и продуманная архитектура уменьшают риск фундаментальных ошибок. Ревью кода и единые стандарты делают код понятнее и легче для поддержки. Автотесты и анализы помогают ловить регрессии и уязвимости, а формальная верификация применяется там, где цена ошибки — человеческая жизнь или миллионы долларов. Системы контроля версий, например Git, позволяют отслеживать изменения и быстро откатываться к стабильным версиям.
Жизненный цикл ошибки
От момента обнаружения до окончательного закрытия баг проходит несколько этапов:
- Новая.
- Назначена.
- Открыта.
- Исправлена.
- На тестировании.
- Проверена.
- Переоткрыта.
- Отклонена.
- Отложена.
Этот цикл обычно отслеживают при помощи систем трекинга багов, которые помогают командам координировать работу, видеть текущий статус дефектов и управлять приоритетами исправлений.