Сложный код, плавающие баги и инсайт

// Февраль 7th, 2011 // Doctrine, PHP, Веб-разработка

Мы три дня ловили баг. Есть такая противная категория ошибок — плавающие баги.  Гейзенбаг (англ. Heisenbug) — термин, используемый в программировании для описания программной ошибки, которая исчезает или меняет свои свойства при попытке её обнаружения. Это слово, в отличие от слова «баг», в русском языке практически не используется. Не полностью идентичный, но достаточно близкий по значению русскоязычный термин — «плавающая ошибка». Примером могут являться ошибки, которые проявляются в окончательном варианте программы (релизе), однако не видны в режиме отладки, или ошибки синхронизации в многопоточном приложении.

Обычно при отладке такого бага, очень трудно его локализовать, т.е. повторить, добиться его стабильного возникновения. Он то появляется, то исчезает. Программист проверяет код, и думая, что он уже отлажен делает коммит на продакшен сервер (ну или на тест-сервер). А потом тестировщик (привет Денис:) обнаруживает его, и возвращает программисту. Тикет по этому багу гуляет из «test» в «new» и обратно по многу раз. Это утомляет всех.
Как правило, при анализе ошибок такого типа, приходится полностью перебирать движок, предлагаются самые неимоверные варианты, по дороге находится и исправляется куча других багов, но этот восстает аки Феникс. Тогда для меня становится делом чести прищучить его. Люблю сложные задачки :-)

Эта ошибка состояла в том, что периодически на одной странице веб-приложения отключался автозагрузчик классов. До этого он работал, везде он работал, а на одной странице брал и отрубался. Через раз. Иногда мог по 6 раз работать, а потом выключиться. Мы начали грешить на кэширование — отключили. Опкод кэшер — тоже выключили. Потом при ручном отключении конкретных подсистем кэша, всё было нормально, начали включать по одной, всё работало. Включили все — работает. А не должно бы. Тикет пошел в «test», но потом вернулся обратно. «Опять !@#$!@$ глючит» — сказал нам тестировщик. Мы долго думали.

Были сделаны самые невероятные предположения — операционная система, броузер, броузер и операционная система, время суток, погода, дайте нам хоть что-то, за что можно зацепиться! Но нет. Вечер пятницы, а ошибка, так и не исправлена. Посудив, что утро вечера мудренее, мы отправились отдыхать. Благо в субботу у нас намечался корпоратив, во время которого мы славно отдохнули, проветрив мозги.

Иногда бывает так, что ты усиленно думаешь над проблемой, а она не решается. Магии в PHP нет. Есть логика. Как говорил один мой преподаватель — «машина она железная, чего ей скажешь — то она и делает». Так что надо искать условия, проверять, думать.

У меня есть «правило Ч«, которому научила военка. Например, я всегда жду людей строго определённое время (которое конечно зависит от отстоятельств). Это избавляет от терзаний вида, «ну подожду ещё 5 минуточек, может придёт». Время вышло, я ущёл. Тоже и с кодингом, нельзя биться головой в стену, есть определённое время на решение проблемы, если оно истекло — значит что-то не так, значит неправильно действуешь.

Когда бьешься долго над такими типами ошибок, пытаешься представить себя машиной, интерпретатором, пропуская через свой разум код программы, вспоминаешь связи между кусками кода, модулями и классами, моделями и видами, контроллерами и действиями. Ты загружаешь программу в своё сознание. Не задумывались об этом? Ошибка не решается, вы идёте гулять/курить/в магазин/гости/бассейн/проветриться и перестаете думать над этой проблемой. Она вытесняется из фокуса сознания. И тут вступает в игру подсознание. Ты вроде бы не думаешь о проблеме, но иногда на тебя словно спускается озарение, инсайт, идея, которую тут же хочется проверить, попробовать, а вдруг — это решение!

Сейчас был именно этот момент. А ведь в тексте ошибки, которую показывал PHP в путях были пути, которые выставляет утилита, а не сам движок. Тут надо сказать, что кроме большого движка веб-приложения, у нас есть несколько утилит, которые делают какую-нибудь полезную работу, они маленькие быстрые (потому что не загружают Zend Framework) и поэтому мы используем их для ряда служебных задач. НО, они совсем ничего не знают о моделях, контроллерах, видах и т.д. Они не умеют грузить модели, у них свой автозагрузчик классов. Стоп! Свой автозагрузчик. А может быть где-то в коде движка инклудятся утилита, которая грузит автозагрузчик, а он затирает пути set_include_path() к классам, которые можно грузить автоматически. Да, это оказалось так. Эврика, инсайт! За это я и люблю свою работу!


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

Что же после всего хочется сказать?

1. Не делайте ваш код сложным. Поменьше магии, нам в работе итак её хватает. Эй, помееьше магии в прямом смысле! Например, в Doctrine есть магические функции вида $model->findByUserIdOrSessionId($user_id, $session_id).  Красиво? Да, но такой функции нет. Что происходит, когда она вызывается. Происходит вызов служебной функции __call(), затем вызов класаа, поиск по массиву (практически парсинг названия функции), составление DQL-запроса, его выполнение и возврат. Сколько лишних операций, зато красиво. Что надо вам? С головой подходите к работе, делайте ваш код понятным, но не перебарщивайте с магией.
2. Просчитывайте варианты. Пытайтесь думать, как машина, смотрите по каким ветвям может пройти программа.
3. Не делайте код сложным. Упрощайте! Делайте функции с говорящими именами, длинные методы разбивайте на маленькие, используйте принцип DRY в своей работе!
4. Отдыхайте! Нашим мозгам тоже нужен отдых, отвлечься от работы очень часто бывает гораздо полезнее, чем сидеть всю ночь над кодом!

Литература

http://trenings.ru/OnLine_treningi/PsihoTrening/PsihoTrening_5-e_zanyatie._Fokus_vnimaniya.html

Share

Спасибо!


Если вам помогла статья, или вы хотите поддержать мои исследования и блог - вот лучший способ сделать это:


2 Responses to “Сложный код, плавающие баги и инсайт”

  1. Александр:

    Интересно было прочитать

  2. Наталья:

    как раз сейчас пытаюсь отловить . Не возможно повторить, чтоб хоть как-то локализовать место поиска.
    Расчет стоимости доставки глючит примерно в 7%.
    Причем не у меня. Я уже сделала заказов 20 в разных броузерах, с разными типами…
    не понимаю, как найти.
    Тоже zend — controller, но передача данных через ajax

Комментировать