Пионеры, поддержка и доктор Хаус или как ловить баги
в рубрике Технологии , теги: баги
- Не дай бог мне попасть на поддержку! Сидишь, правишь чужие баги…
- …господи, да этот код писали полные идиоты!..
- …совершенно невозможно в этом дебилизме разобраться! Единственный выход - все к черту переписать! Лучше сразу на всякий случай переписать, а то через несколько лет…
- Эх, вот бы иметь документацию! Такую, чтоб прочитал три страницы за три минуты, и все понятно стало! Да по любому модулю!
- …Не, это разве документация? Во-первых, тут не три страницы, а сто пятьдесят, во вторых, ни черта не понятно, а в третьих - все работает не так!!!
- …и все-таки, этот код писали полные идиоты!..
- …что, мелкую фичу добавить к этому коду? Не, я это трогать не буду. Разобраться ж невозможно, да и писали идиоты. Я лучше сбоку вот тут такое приляпаю… Хорошее, большое, и задизайню как следует! Не то, что идиоты…
- …что, еще и эту фичу? Разбираться? Вы издеваетесь? Да мы это сверху захачим.
- …не, этот дефект исправить нельзя. Только если все переписать. Как так - появился после добавления моей фичи? На что это вы намекаете?
- …Какая такая ПРОСТАЯ фича? О чем это вы говорите? Да чтоб к ЭТОМУ ее добавить, полгода надо, не меньше. Чтоб все переписать. А что вы хотите?..
- …ну и что, что это фича касается моего нового, хорошего кода! Это не он виноват, он хороший! Это виноват тот, старый, плохой код, писанный идиотами! Да черт его знает, как он работает! Я ведь вас предупреждал, надо все переписать! Все!
- …эх, мне б новый проект! Вот где простор для НАСТОЯЩЕГО творчества!
- …кто это идиот?! Это Я код писать не умею?! МОЙ код весь переписать?! Да ты что, новичок, охренел? На, сам напиши, попробуй! Что значит, не документирован? Нет у меня времени доки писать, программист я, не писатель! Читай код! Что?! В ООН жалуйся, Кофи Аннану!!!
***
Вот так, дорогие сэры. Поддержка традиционно имеет среди молодых программистов, не писавших ничего серьезного и не работавших в больших группах, репутацию грязной, ни разу не креативной работы. Код проще переписать, чем в нем разбираться. Это удобно еще и потому, что переписывание - это тот самый “креатив”, которого они так хотят. И совершенно не важно, насколько хорош старый код, и кем он написан, он плох только тем, что в нем надо разбираться, и “исправлять чужие баги”. Это почитается унизительным. Одно дело - свои. А тут - чужие! Кошмар!
Да, а еще молодые программисты знамениты тем, что могут написать невероятно много кода в крайне сжатые сроки. Скажем, две тысячи строк кода за выходные запросто могут написать. И они этом очень гордятся. В понятие “написал”, разумеется, вкладывается колбасение изолированного кода, не связанного никаким окружением, практически без какого-либо предварительного проектирования, и с отладкой его на паре основных тест-кейсов, без учета каких-либо граничных условий.
Молодому программисту, на самом-то деле, не интересно ловить не только чужие, но и свои собственные баги. Ему интересно творить. Думать ему перед написанием кода тоже не особо интересно, ибо написание кода ему в новинку, представляет собой для него бешеный креатив, и поэтому кода пишет он много. И написание кода ему нириально доставляет. В отличие от отладки, диагностики проблем, написания документации. И конечно, тягостных раздумий о том, как написать меньше кода, и чтобы в нем было поменьше проблем. Последнее для него совершенно противоестественно. Так уж он устроен, молодой программист.
“Молодость” программиста, впрочем, есть скорее состояние души, чем стаж в индустрии или физиологический возраст. Пионер, он, как известно, всегда остается пионером, и он всегда готов!
Эх, пнимаешь, молодость.
И не надо мне говорить, что вы не были такими :). Я, конечно же, охотно верю, что вы потратили на предварительное проектирование своей первой программы не менее 30% времени, что вы покрыли ее как white box, так и black box тестами, учтя граничные условия и обеспечив хорошее покрытие, а потом, конечно же, аккуратно задокументировали результат :). Я допускаю даже, что вы были лишены искренней радости от наблюдения того, что вы, по сути, наблюдаете маленькое чудо, - ваша программа работает на паре тестовых кейсов. И что эта радость отнюдь не была вашей первой мотивацией к тому, чтобы программировать, и вы с самого начала делали это уныло и “промышленно”.
Это, в сущности, прекрасно, и ничего плохого в этом нет. Дело, однако, в том, что цель и реальность промышленного программирования немного другие. За программу должны заплатить деньги те люди, чью потребность она удовлетворяет. Это никак не конфликтует с целью получения удовольствия от программирования, даже напротив, факт того, что результат нашего труда людям полезен, и они его ценят - также нириально доставляет.
Однако, проблема в том, что эти странные люди вовсе не разделяют нашу радость от работоспособности программы на основном кейсе. Более того, они:
1) негодуют по поводу неожиданных багов, возникающих в самых неожиданных местах. Они, негодяи, так неожиданно нашу программу используют!
2) и кроме того, они хотят от нас новых фич и новых кейсов! Они хотят, чтобы программа умела больше, и делала все лучше! Постоянно хотят!
Здесь и состоит загвоздка. Реальность промышленного программирования состоит в том, что любой коммерчески успешный (читай - удачный, нужный людям) проект достаточно быстро выходит из фазы первой версии, и превращается в то, что молодые программисты называют “поддержкой”. А на самом деле - является не только поддержкой, но и развитием.
Плохие, негодные проекты никогда не доходят до этой фазы, мои уважаемые коллеги.
Ибо суть они ни кому не нужное УГ, вызывающее зевоту у пользователей. И чем более удачен и успешен проект, тем ДОЛЬШЕ он будет находится в состоянии поддержки и развития. “Поддержка” - это признание. И при поддержке хорошего продукта, его приходится развивать. Добавляя к нему фичи.
То есть, ВСЕ успешные проекты быстро попадают в поддержку, и, таким образом, БОЛЬШИНСТВО проектов по факту делаются по смешанному циклу - поддержка и развитие. То есть они развиваются ИНКРЕМЕНТАЛЬНО, в них постоянно правятся дефекты, и добавляется большое количество новых фич. Вероятность “попасть на поддержку” для молодого программиста, таким образом, крайне высока. Как ни прискорбно это признать.
Удивительно, кстати, что традиционное “управление проектами” уделяет столь мало внимания этому жизненному факту, являющемуся кроме всего прочего признаком успеха продуктовой компании в нашей индустрии, концентрируясь на новых проектах “с чистого листа”. Надо уметь управляться в той реальности, в которой мы живем, не так ли? Однако в этот раз я хочу рассказать не об управлении разработкой при цикле “поддержка и развитие”, а о том, что данная реальность вовсе не так тягостна для молодых программистов, как им кажется.
Дело в том, что писать код - это не самое большое удовольствие. Это ерунда, слабенькое средство, от которого торкает не так сильно. Истинное удовольствие, гораздо более сильное, подсев на которое, сложно слезть - кроется именно в нелюбимой ими “поддержке”.
Да вот взять хотя бы меня. Проработав 6 лет в цикле поддержки и развития, и будучи “чистым” менеджером последние 4 года, единственное, мимо чего я не могу пройти спокойно мимо - это когда парни, матерясь, ловят злую багу! Волной накатывает охотничий азарт, и я сразу понимаю, как же я по этому соскучился!
Вы смотрели сериал “доктор Хаус”? Вы читали детективы про Шерлока Холмса? Значит, вы можете понять, на что похожа ловля багов, уважаемые коллеги. И на кого похож профессионал в этом деле. Холмс, как вы догадались?! - Это элементарно, Ватсон. Для человека, владеющего дедукцией, не составит труда понять, что Вы прибыли именно из Афганистана. Без всяких логов, отладчиков, и прочей ерунды.
Я попробую систематизировать свой опыт правильной ловли багов. Хотя, конечно, это надо делать раньше, сейчас я многие нюансы забыл.
Итак. Первое - это цикл работы с ошибкой. Ошибка, уважаемые коллеги, исправляется в три этапа. Данные этапы - закон природы, который не обойти. Ну или почти - есть исключения.
1) Первая цель - это понять проблему
2) Установить причину проблемы
3) Исправить проблему
1) Первая цель - понять проблему. То есть, по завершению данной фазы, вы согласились с тем, что это именно дефект, а не ожидаемое поведение. Обыкновенно, вы к концу данной фазы умеете его воспроизводить. В любом случае, обыкновенно, вы к концу данной фазы собрали достаточно информации о проблеме, чтобы работать над ней по большей части самостоятельно.
Это - ключевое. И это подразумевает наличие краткого и недвусмысленного описания дефекта. Наличие четкого описания “симптомов болезни” и максимально простой процедуры воспроизведения - также критерий завершения данной фазы.
В течение этого этапа вы исследуете проблему в разных позах. Иногда этот этап занимает минуты - вы сразу воспроизводите дефект, и без вопросов понимаете - это дефект. Иногда - данный этап длится достаточно долго, и сопровождается интенсивным общением с человеком, обнаружившим проблему.
На данном этапе для успеха критичен один важный навык, которого лишено большинство программистов, ибо он не имеет прямого отношения к программированию. Он относится к общеинженерным навыкам проведения экспериментов. Дело в том, что обычно дефект, который найден пользователем, не сопровождается краткой и четкой инструкцией по воспроизведению.
Вместо этого, вам сообщают длинную последовательность действий, где проявляется непонятное поведение. Ваша задача - максимально упростить тест-кейс, отбросив лишнее, и составить минимально возможную последовательность действий, которая воспроизводит проблему. И демонстрирует ее наиболее ярко, без лишней шелухи.
Это сэкономит вам _огромное_ количество времени на следующем этапе. В сущности, люди, которые в совершенстве владеют указанным навыком, вселяют в души остальных священный трепет. Ибо, в отличие от остальных, они по окончании данного этапа уже ЗНАЮТ, где искать проблему. Потому, что они ее _локализовали_ еще до того, как заглянули в код.
Это - ключевой навык на данном этапе. Иногда один он способен творить чудеса. Активная “отладка” уже начинается, когда мы только наблюдаем проблему.
В любом случае, пока вы не поняли проблемы, вы не сможете завершить следующий этап. Начать его - сможете, а вот завершить - никак. Ибо второй этап завершается, когда…
2) Установлена причина проблемы.
Это когда вы в состоянии объяснить, почему происходит проблема, и что ее вызывает. Вы можете показать по шагам, как и почему она проявляется. Далеко не всегда установление причин происходит быстро.
Здесь работает цикл гипотеза-эксперимент. На данном этапе вы сочетаете основной навык из этапа (1) с другими, как-то - умение читать код.
В умении читать код с целью диагностики ошибок главное - понять, в чем вам НЕ НАДО разбираться. Вы читаете тысячи строк кода в день. Иногда - просматриваете десятки тысяч. Понять их вы не в состоянии. И не надо пытаться - схватите сенсорную перегрузку. Вам надо научиться понимать, на что вам действительно надо обращать внимание, а на что нет.
Даже в случае, если вы сидите в отладчике - поток управления заносит вас в самые отдаленные и незнакомые вам куски системы. Вы должны уметь разобраться а) где вы находитесь б) о чем вообще это, и главное - в) надо ли в это вникать глубоко.
Так вот. Лучший (и единственный действительно работающий) способ читать тонны кода - это “активное чтение”. Поставьте себя “на место преступника”. Вы понимаете проблему. Вы знаете ее симптомы. Что, если вы специально хотите ее внести, такую багу? Что вы сделаете? Куда будете вносить правки?
Ах, вы не понимаете этой подсистемы, и не можете ответить на данный вопрос? Отлично. Пришло время учится скоростному reverse engineering. Знаете, какой самый быстрый способ понять чужую подсистему? СПРОЕКТИРУЙТЕ ЕЕ.
Эта попытка не обязана окончится успехом. Однако попробовав это сделать, вы быстро обнаружите проблемы дизайна, в которые вы заткнетесь, и “вилки” в принятии решений - разные подходы, какие мог испробовать ее автор.
Вот здесь - внимание! - вы прерываете процесс проектирования, и смотрите в код. С вполне конкретной целью - понять, какой же подход выбрал автор. Вы удивитесь, до какой степени возрастет продуктивность чтения кода при таком подходе. Вы теперь не тупо пялитесь в него, читая все подряд - вы быстро его просматриваете, и ищете ответ на конкретный вопрос. Сделано может быть так, так, и так. Как сделано? О как, оказывается! Хренассе, с фантазией! Вот так надо читать код.
Иногда читая код, вы не понимаете, что делают эти несколько строк, ибо это есть с вашей точки зрения полный бред. Не надо их удалять! Вообще ничего не трогайте, если не понимаете! Вместо этого, воспользуйтесь редким навыком - посмотрите в историю коммитов. Сделать это просто - откройте файл в VCS в режиме annotate, и вы увидите автора правки, ссылку на коммит. Ткните в нее, и вы увидите комментарий.
Даже если в комментарии к коммиту будет стоять просто номер дефекта - найдите этот дефект и почитайте. И вы, скорее всего, узнаете много нового о требованиях к системе. В старых системах до половины требований содержатся именно в базе дефектова и фичреквестов. И волшебным образом, если у руководства хватило ума настоять на этом правиле, в комментарии к коммиту всегда будет ссылка на тикет, что обеспечит вам безболезненное чтение кода.
Поэтому, ВСЕГДА ПИШИТЕ КОММЕТАРИИ К КОММИТАМ! Это и есть та самая важная документация, которая выручает при чтении кода! Даже простой номер дефекта уже помогает. А вообще - правило хорошего тона - добавлять в комментарий описание способа исправления дефекта - как именно вы его правите.
Обеспечить выполнение этого правила проще, чем многим кажется. На коммит вешается триггер, который посылает письмо с текстом комментария в общую рассылку на адрес, скажем, checkin. На нее в обязательном порядке подписываются все. Это естественным образом приводит к тому, что люди начинают писать содержательные комментарии. Кроме того - если вы введете правило, что в комментарии указывается фамилия человека, который проводил код ревью, то это поможет еще больше. Кроме того факта, что у вас будет код ревью.
Во всем перечисленном есть одно общее - вы ставите гипотезу - вы ее проверяете. Гипотеза - эксперимент. Проектируя код, и натыкаясь на проблему проектирования - вы ставите гипотезу относительно возможного решения - и проверяете ее чтением кода. Так читают код. Ловя багу, вы ставите гипотезу относительно ее причины - и проверяете ее тестом.
Итак, ключевые навыки на данном этапе:
- умение локализовать проблему тестами, как и на первом этапе
- умение быстро проектировать в голове
- умение быстро читать код, опирающееся на опыт, плюс навык проектирования.
- разумеется, разнообразные экспериментальные навыки, такие как пользоваться отладчиком, ставить условные брейкпойнты, пользоваться логами и отладочной печатью, и ставить ассерты. Это большая и отдельная тема - но это частности, и это по большей части все умеют. Хотя, есть редкие нюансы касательно ассертов и логов. Ассерты позволяют реализовать технику инвариантов и предусловий-постусловий, и логи можно делать весьма креативно. Скажем, я предпочитал писать логи в реляционном виде, с фиксированными полями разделением табуляцией, и обрабатывать их SQL-запросами из Access, с применением join-ов и иногда group by. Но это детали.
Иногда, когда вы поняли причину проблемы, решение тривиально. Но иногда - вы понимаете, почему происходит проблема, но как ее исправить надо поломать голову. И далеко не всегда после завершения этого этапа, вы можете быстро завершить следующий этап, разработав фикс.
3) Проблема решена.
Фикс типо есть. Мы сделали фикс, проверили - и он исправляет проблему. Круто.
Здесь, в случае если вы правильно проделали все предыдущее, ничего необычного. Это самая обыкновенная разработка. Однако есть нюансы.
В сериале «Доктор Хаус» доктор часто пробует разнообразные решения для того, чтобы понять, в чем проблема. В нашем случае так часто делают “быстрые” программисты, которые “лечат болезнь, а не больного”. И даже не болезнь. Хуже того - симптом.
Пример. Программа неправильно выводит цвет некоторых точек. Красный, а должен быть синий. Нивапрос! Пропуская промежуточные этапы, вставляем код: если цвет, понимаете ли, красный, когда должен быть синий, то он, будет-таки синий.
Такие исправления обычно создают новые баги, которые обнаруживаются позже, и не связываются с данным исправлением. Кроме того, подобные исправления усложняют поведение программы, и приводят к появлению диких, неожиданных дефектов в случае добавления новых фич.
Они повышают “хрупкость кода”, и цену внесения последующих изменений. Так делать не надо, если у вас нет веских причин так делать. Вообще, они могут быть, эти причины. Я один раз так делал для критичного дефекта, к которому не смог завершить этап (2), чтобы понизить severity проблемы с креша на feature failure. Всегда, знаете ли, найдутся проблемы, которые вам не по зубам.
Комментариев: 3 на “Пионеры, поддержка и доктор Хаус или как ловить баги”
Прокомментировать
Вы должны быть авторизованы для комментирования.




Дементьев Максим:
Толковый материал. Прочёл с удовольствием. Поддерживаю, что поиск багов, т.е. особенно на стадии (1), увлекательнейшее занятие.
Некоторые моменты процесса хотелось бы даже обсудить. Потому что как раз сейчас провожу много времени, эмулируя нарушение нормальных граничных условий для основных прецедентов. Оптимизация какая-то здесь была бы очень кстати.
7 октября, 2009 в 12:37
Балин Владислав:
Здравствуйте Максим,
что именно Вам хотелось-бы обсудить?
16 октября, 2009 в 0:40
Дементьев Максим:
Обсудить хотелось бы нек. моменты, связанные с тестированием.
Но в данный момент сии моменты (точнее, создание адекватной проекту среды тестирования) отошли на задний план. Поэтому обсуждение откладывается на неопределённый срок.
Читаю пока Ваш ЖЖ, очень интересно и, главное, практично. ПС. Вовсе не реклама :).
3 ноября, 2009 в 10:56