Если отладка это избавление от ошибок, то программирование - их создание.Программы без ошибок бывают. Не бывает программистов, которые не делают ошибок. Между этими взаимно противоречивыми утверждениями есть связующее звено - исправление ошибок.
Ошибки происходят при выполнении ошибочного кода. Если выполнение не прошло через ошибочный код, то ошибка не происходит. То, что код работает и ошибка себя никак не проявляет, не должно говорить о том, что там нет ошибок. Сбой в модуле "который всегда работал" может проявиться при внесении изменений в совершенно другом месте.
Ошибки проявляются после того, как происходят. Расстояние во времени или в выполняемых программой действиях может быть как нулевым (скажем, деление на ноль), так и очень большим (записали в файл не тот байт, а когда он еще прочитается - это вопрос).
Чем нужнее и ответственнее назначение программы, тем больше в ней должно делаться для ошибок. Суть программы для программиста - не столько выполнить программой какие-то действия (это несложно), сколько корректно отнестись как к внутренним ошибкам (сделанным программистом), так и к внешним факторам мешающим работе программы.
Программу лучше писать для отладчика. Пишется сам код недолго, а вот борьба с проблемами может занимать все время жизни и эксплуатации программы или библиотечных модулей. Не надо выкрутасов с языковыми конструкциями, сделай так чтобы проще было отловить ошибку. Считай, что каждый твой оператор содержит ошибку. Оптимизацию лучше отложить, главное чтобы выбранная архитектура ее допускала.
Информацию об ошибках надо собирать и анализировать. По результатам анализа делать выводы. С чем именно коррелирует происхождение ошибки. Независимо от частоты происхождения проблем какого-то рода нужно внимательно относиться ко всем факторам, с которыми коррелируют ошибки. Они могут быть самыми необычными. Если видишь один из факторов с которыми устойчиво коррелируют ошибки - готовься, скорее всего ты уже создаешь ошибку.
Нужно помнить, что развитие некоторых программ - это их нормальное состояние. Поэтому фактор "а не надо было менять ТЗ" может оказаться самым неподходящим. Исключение - если первоначальное ТЗ создавалось с клятвенными уверениями, что именно это и только так будем делать. Например, что ни за что не будем превращать конвертер отчетов во всепогодный драйвер ODBC.
Ошибки могут быть как заложены в архитектурное решение, так и возникать вследствие банальной опечатки или забывчивости выполнить один-два оператора на тысячу строк. Ошибки бывают самыми разными и для каждого вида ошибок могут понадобиться разные методы борьбы. И могут быть ошибки, для которых методы борьбы еще неизвестны.
При отлове ошибок вероятность найти следующую уменьшается по мере уменьшения числа ошибок. Чем дальше - тем труднее и дольше искать следующую. Время между исправлениями двух ошибок увеличивается. Со временем оно может вырасти до очень большого, и может быть принято решение о соответствии программы так называемому "допустимому уровню ошибок". Такое бывает. Просто соглашаемся на то, что там есть ошибки, но на наших задачах они либо не проявляются либо их проявление не приносит ощутимого ущерба.
Если функционал программы ограничен, и задача, стоящая перед программой, не изменяется и архитектура программы соответствует ее назначению, то со временем число ошибок убывает примерно по логарифмическому закону. Если построить график исправления ошибок в зависимости от времени, то можно примерно оценить асимптоту, к которой приближается кривая числа ошибок. По текущему расстоянию до асимптоты можно оценить, сколько еще проблем в программе.
Если программа не имеет ограниченного функционала и он постоянно расширяется и назначение программы изменяется, то асимптота превращается в наклонную линию, и как быстро она будет возрастать - это почти непрогнозируемо. Приближаться к горизонтальной асимптоте проще, чем к растущей. Помни об этом, когда есть выбор делать все в одной постоянно изменяемой программе или разбить на изолированные друг от друга модули, четко выдерживающие свое назначение.
В одной из наилучших в отношении надежности программ операционной системе Solaris количество ошибок оценивается примерно около 1 ошибка на 10-50 тысяч строк кода. Сопоставь со своей программой. Если в твоей программе есть 100 тысяч строк, то когда добьешься уровня качества и стабильности Solaris, в твоей программе еще останется с десяток ошибок.
Каждое изменение назначения и функционала программы есть 1) прибавление к программе фрагмента еще одной программы, 2) переделывание имеющихся частей программы и 3) возможное изменение архитектуры. Изменение ТЗ приводящее к изменению архитектуры есть внесение в программу ошибки архитектурного уровня, которые самые дорогие. Поскольку после внесения изменений в ТЗ имеющаяся архитектура становится либо полностью либо частично ошибочной.
Ошибок не надо бояться, их обнаружению надо радоваться. Чем дальше - тем сильнее, потому что обнаружение ошибки есть большой труд и если ошибка обнаружена - значит этот труд успешен.
Если изначально известно, что программа будет по своему функционалу изменяться, это следует закладывать в ее планируемую архитектуру.
Часть ошибок может быть выявлена до выполнения программы, если применяются специальные технические средства. К ним относятся:
- транслятор с синтаксическим контролем
- типизация в используемом языке
- уровень предупреждений транслятора, обнаруживающий потенциальную проблему
- проверочные действия (ассерты) значений и операций, неверность которых есть ошибка программирования
- проверка возвращаемых значений, изменяемых состояний и отлов эксепшенов, которые есть ошибки времени выполнения
- тесты, результат выполнения которых известен
- дамперы состояния памяти и файлов с ручным анализом
- вспомогательные утилиты автоматического просмотра состояния памяти и файлов
- средства перехвата выполняемых функций, анализирующие корректность параметров и очередность вызова функций
Все ошибки должны быть внесены в базу ошибок. Информация не должна быть потеряна, чтобы была возможность вернуться к этой ошибке. Суть не в том, как именно выполнена эта база, а в том, что она рабочий инструмент. Насколько инструмент будет интегрирован с другими - вопрос вкуса. Но свою задачу он должен выполнять четко.
Исправлять надо начинать ошибки из тех, которые наиболее простые. Потому что мелкая ошибка может приводить к возникновению другой, крупной. Мелочность или важность ошибки - вещь условная и эмоциональная. Ошибкам на самом деле плевать на наши эмоции. Локализовать сильную (или вызывающую наибольшие эмоции) ошибку проще, если окружающий ее контекст содержит меньшее количество ошибок.
В действительности мы всегда должны различать происхождение и проявление ошибки. Между ними есть расстояние во времени и в месте. Одна и та же ошибка может проявлять себя в разных местах. Будучи по сути простой, она может приводить к сбою критических для назначения программы элементов. Если модуль чтения обнаружил проявление ошибки, то необязательно она произошла в модуле записи. Это может быть любой другой модуль, который произвел неверную адресную операцию. Если бы у нас были цветные байты, то можно было бы отмаркировать от какого модуля куда попали байты. Но у нас нет таких байтов.
Если мы видим проявление ошибки, то для исправления ее мы должны найти место ее происхождения. Это место находится где-то от начала запуска программы до непосредственно места проявления ошибки. Локализовать надо сначала не столько место, сколько момент происхождения. По моменту происхождения локализовать место. И вовсе не обязательно, что ошибка произошла в только что написанной и добавленной функции.
Если видишь проявление ошибки - не паникуй. Ты - последняя инстанция в решении проблемы. Если не ты, то больше никто. Чудес не бывает, ошибки исправимы.
Комментариев нет:
Отправить комментарий