Как исправить любой баг? | SHIFU.IO
Статьи
Как исправить любой баг?
Перевод 23.02.2018

Источник: Duncan Riach, Ph.D. 

Быстрый и эффективный поиск и устранение ошибок как в новых, так и в давно созданных системах - один из самых ценных инженерных навыков, которые вы можете прокачать. Тем не менее, этот навык редко оценивается на собеседованиях, потому что его сложно структурировать и задокументировать. Я находил и решал много сложных проблем в большом количестве разных систем, включая сверхсложные компьютерные процессоры, многопоточные сервера и приложения, а также в проблемных людях, семьях и организациях. Оптимальная процедура поиска и исправления ошибок по сути одинакова во всех сферах. Удивительно, но многие разработчики программного обеспечения не имеют чёткого понимания процесса, и сейчас я намерен восполнить этот пробел. Вот мой трактат по дебагингу:

Шаг 1: Определите, что точно работает.

Если что-то работает не так, как ожидалось, можно подумать, что сломано вообще всё. Но потратьте немного времени, чтобы найти то, что действительно ломает всю систему. Это поможет локализовать проблему, и вы будете чётко понимать её границы.

Шаг 2: Определите точно, что не работает.

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

Шаг 3: Упростите проблему

Проблемное поведение в сложной ситуации может быть трудно воспроизвести или обобщить, особенно когда есть недетерминированные или статистические эффекты. Любая попытка упростить тестовый пример, сохраняя при этом проблемное поведение, будет полезна. Например, если проблема возникает при обработке очень большого набора данных, вы можете попытаться воспроизвести проблему с меньшими наборами данных. Конечно, этот пример невозможен, если проблема связана с большими наборами данных. В этом случае создание простого, но все ещё большого набора данных может иметь больше смысла. Поэтапно анализируя ситуацию при возникновении проблемы, вы не только лучше понимаете, что делать в текущей ситуации, но и начинаете предугадывать появление других проблем. Простые тестовые примеры полезны для точной связи ошибок с другими частями, чтобы быстро проверить, влияют ли изменения на ошибку, а также могут стать частью ваших «антирегрессионных» тестов (шаг 7). Поскольку простые тестовые примеры обычно можно запускать быстро, они также поддерживают тестирование гипотез (шаг 5).

Шаг 4: Придумайте гипотезу

Вы можете прийти к решению через несколько минут, часов, дней или даже недель работы. Независимо от того, как вы до него дошли и сколько времени это заняло, у вас теперь будут данные, и вы узнаете что-то о том, как проявляется проблема. Это знание позволит вам сформулировать гипотезы о том, что может вызвать проблемы - какой процесс внутри (или даже вне) системы может к ним привести.

Шаг 5: Протестируйте гипотезы, используя принцип «разделяй и властвуй»

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

В этот момент можно применять принцип «разделяй и властвуй» очень наивно: произвольно разбивать систему на две половины, искать проблему в каждой половине, а затем рекурсивно увеличивать на неверно функционирующей половине. Я не рекомендую этот подход, потому что он обычно очень медленный и громоздкий. А с помощью гипотез можно сэкономить время и усилия, используя деление, описанное выше. Вы по-прежнему проверяете, является ли поведение ожидаемым как раз там, где вы предполагаете проблему, и если гипотеза подтверждается, вы переходите к следующему блоку. Это позволяет очень быстро масштабировать ошибку. Переходите к следующему шагу, как только вы поняли в чём ошибка.

Шаг 6: Подумайте о других ошибках этого класса

Иногда ошибки вызваны простыми опечатками или разовыми недоразумениями, и эти ошибки могут быть исправлены изолированно. Тем не менее, для багов гораздо более показательно распространение в большом классе проблем.

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

Мы же не хотим приклеить пластырь к злокачественной опухоли и отправить пациента домой.

Шаг 7: Создайте «антирегрессионные» тесты

Даже если вы не разрабатываете системы, использующие тестовую разработку, я рекомендую вам использовать исправление ошибок с помощью теста.

Убедитесь, что в написанных модульных и/или системных тестах будут выполняться как можно больше ошибок данного класса. Основная причина, по которой ошибка существует, заключается в том, что не было никаких тестов, чтобы ее поймать. Это означает, что в наборе тестов есть дыра. Я часто говорю: "если что-то не проверить, оно обязательно сломается". Вы должны предположить, что оно либо сломано сейчас, либо сломается однажды в будущем, а первым, кто обнаружит поломку будет именно клиент. Поскольку у вас есть сломанная система прямо сейчас, это отличная возможность для разработки тестов и обеспечения их отказа. Эти возможности часто возникают, поэтому хватайте их, пока они доступны. Мне нравится называть регрессионные тесты «антирегрессионными», потому что они препятствуют регрессу продукта в более раннее, разбитое состояние. Запустите свой тестовый пакет со всеми вашими тестами, прежде чем выпускать новые версии вашего продукта.

Шаг 8: Исправьте баг (или баги)

Если вы действовали по списку и были прилежны, исправление ошибок теперь будет простой формальностью. Такое исправление ошибок можно выполнить очень спокойно и уверенно. Однажды я был свидетелем того, как инженеры работали абсолютно по-другому: они меняли код почти случайным образом, надеясь, что это устранит общую проблему. Такой подход, скорее приведёт к появлению новых ошибок, чем к исправлению существующих.

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

Шаг 9: Убедитесь, что теперь тесты работают

Все новые тесты должны быть пройдены. Если они не проходят, вам нужно будет вернуться к предыдущему шагу и решить проблему.

Шаг 10: Проверьте простую часть

На этом этапе возможно запустить простые тестовые примеры, которые вы разработали на третьем шаге, и они должны работать должным образом. Если нет, вернитесь к более раннему этапу, чтобы решить проблему.

Шаг 11: Проверьте изначальную ошибку

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

Шаг 12: Задокументируйте исправление

Вы только что выполнили чрезвычайно качественный набор инженерных маневров. Возможно, вы единственный человек, который осознаёт весь героизм ваших поступков. Запишите их так, чтобы они могли стать частью инженерных знаний. Сохраните код документа, задокументируйте план тестирования, напишите страницу вики или сообщение в блоге. Сделайте что-то, чтобы сохранить мудрость, которую вы разработали, и открыть её для других. Ваша документация будет также обучать и наставлять других. Вы станете хорошим примером для других инженеров, примером, как эффективно использовать ресурсы, а также о том, как выполнять сложные инженерные работы таким образом, чтобы они были полезными и приятными.

Шаг 13. Проверьте любые другие возможные классы ошибок.

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

Шаг 14: Публикуйте

Публикуйте исправленную системы и убедитесь, что все, кто должен знать, знают, что вы сделали. Кратко изложите проблему и решение и включите ссылки на созданную вами документацию. Готово. Вы великолепны!

Вывод

Вы прекрасно поработали. Пожмите себе руку и отправляйтесь делать что-то ещё выдающееся.

Перевод 23.02.2018