Почему вы должны отлаживать, когда нет ошибки

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

Это не имеет значения!

С совершенно новыми репозиториями исходного кода мы по-прежнему ничего не знаем.

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

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

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

Поиск использования

Язык программирования может очень помочь. Нам, как Java-разработчикам, повезло: инструменты исследования базы кода удивительно надежны. Мы можем копаться в коде и находить применение. IDE подсвечивают неиспользуемый код, и они отлично подходят для этого. Но у этого есть несколько проблем:

  • Нам нужно знать, где искать и как глубоко
  • Код может использоваться тестами или API, которые на самом деле не используются пользователями.
  • Поток трудно понять через использование. Особенно асинхронный поток
  • Нет контекста, такого как данные, чтобы помочь объяснить поток кода

Должен быть лучший способ, чем случайное прочесывание исходных файлов

Генерация UML

Другой вариант — создание диаграмм UML из исходных файлов. Мой личный опыт работы с этими инструментами невелик. Они должны помочь с «общей картиной», но я часто чувствовал себя еще более сбитым с толку этими инструментами.

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

Отладка как средство обучения

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

У меня есть привычка читать значения переменных в часах, когда я использую отладчик для изучения базы кода.

Имеет ли это значение смысл в данный момент времени?

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

Обратите внимание, что я использую Java, но это должно работать и для любого другого языка программирования, поскольку концепции (в основном) универсальны.

Поле наблюдения

Я думаю, что большинство разработчиков знают о полевых точках наблюдения и просто забывают о них!

«Кто изменил это значение и почему», — пожалуй, самый частый вопрос, который задают разработчики. Когда мы просматриваем код, могут быть десятки потоков кода, которые вызывают изменение. Но установка точки наблюдения на поле расскажет вам обо всем за считанные секунды.

Понимание мутации состояния и распространения — это, вероятно, самое важное, что вы можете сделать при изучении кодовой базы.

Возвращаемое значение

Одна из самых важных вещей, которую нужно понимать при переходе по методам, — это возвращаемое значение. К сожалению, в отладчиках эта информация часто «теряется» при возврате из метода и может пропустить важную часть потока.

К счастью, большинство IDE позволяют нам динамически проверять возвращаемое значение и видеть, что метод вернул в результате своего выполнения. В IDE JetBrains, таких как IntelliJ/IDEA, мы можем включить Показать возвращаемое значение метода, как я обсуждаю здесь.

Управление потоком как средство обучения

Зачем нужна эта строка?

Что было бы, если бы его не было?

Это довольно распространенный набор вопросов. С помощью отладчика мы можем изменить поток управления, чтобы перейти к определенной строке кода или вызвать ранний возврат из метода с определенным значением. Это может помочь нам проверить ситуации с конкретной строкой, например, что, если бы этот метод был вызван со значением X вместо Y?

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

Отслеживание с помощью маркировки объектов

Маркировка объектов — одна из тех неизвестных возможностей отладчика, которые бесценны и чрезвычайно эффективны. Это играет большую роль в понимании того, что, черт возьми, происходит.

Вы знаете, как при отладке значения вы записываете указатель на объект, чтобы отслеживать, «что происходит в этом блоке кода?».

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

Мы можем начать отслеживать объекты, чтобы понять поток и посмотреть, как все работает. Например, если мы смотрим на объект User в отладчике и хотим отслеживать его, мы можем просто сохранить ссылку на него. Затем используйте условные точки останова с пользовательским объектом, чтобы определить область системы, которая обращается к пользователю.

Это также чрезвычайно полезно для отслеживания потоков, что может помочь в понимании кода, где логика потоков сложна.

Проверьте объекты в памяти

Обычная ситуация, с которой я сталкиваюсь, — это случай, когда я вижу компонент в отладчике. Но я искал другой экземпляр этого объекта. Например, если у вас есть объект с именем UserMetaData. Есть ли у каждого пользовательского объекта соответствующий объект UserMetaData?

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

Наблюдение за фактическими значениями экземпляров объектов и их просмотр помогает поставить цифры/факты за объекты. Это мощный инструмент, который помогает нам визуализировать данные.

Используйте операторы Tracepoint, чтобы следовать сложной логике

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

Но добавление журнала может быть хуже. Перекомпилируйте, перезапустите и случайно зафиксируйте в репозитории. Это проблематичный инструмент для отладки и изучения кода.

К счастью, у нас есть точки трассировки, которые фактически являются журналами, которые позволяют нам распечатывать выражения и т. д.

Что происходит в производстве — также известное как «Освещение реальности»

Это прекрасно работает для «простых» систем. Но в нашей отрасли есть платформы и настройки, которые невероятно сложно воспроизвести в отладчике. Знание того, как наш код работает локально, — это одно. То, как это работает в продакшене, совершенно другое.

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

Мы называем это охватом реальности. Например, вы можете получить 80% покрытие в своих тестах. Но если у вас низкий охват классов, к которым активно обращаются в вашем репозитории исходного кода… контроль качества может быть менее эффективным. Мы можем изучать репозиторий снова и снова. Мы можем использовать любой инструмент анализа кода и настройку. Но они не покажут нам две вещи, которые действительно важны:

Это действительно используется в производстве?

Как это используется в производстве?

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

Чтобы получить представление о производстве и отладке этой среды, нам понадобится инструмент наблюдения за разработчиком, такой как Lightrun. Вы можете установить бесплатно здесь.

Измерение счетчиками

Счетчик позволяет нам увидеть, как часто была достигнута строка кода. Это один из самых ценных инструментов в нашем распоряжении.

Этот метод вообще достигнут?

Достигнут ли этот блок в коде? Как часто?

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

Проверка предположения с условиями

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

Вы можете проверить это с помощью условных операторов, которые можно прикрепить к любому действию (журналам, счетчикам, снимкам и т. д.). В результате мы можем использовать такое условие, как user.signupDate.getMillis() < ….

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

Журналы и трубопроводы для изучения без шума

Я думаю, очевидно, как внедрение журнала во время выполнения может иметь огромное значение для понимания нашей системы. Но в производстве за это приходится платить. Я изучаю систему, просматривая журналы, и все мои журналы «метод X достигнут со значением Y» добавляют шума нашим бедным командам DevOps/SRE.

У нас этого не может быть. Изучение чего-либо должно быть уединенным, но производство по определению является полной противоположностью этому.

С помощью piping мы можем записывать все локально в IDE и избавить всех остальных от шума. Поскольку логика изолирована, не будет накладных расходов, если вы будете регистрировать слишком много. Так что сходите с ума!

Снимки для более легкого обучения

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

Опять же, использование условных снимков очень полезно для точного определения конкретного вопроса. Например, что происходит, когда пользователь с правами X использует этот метод?

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

Заключительное слово

Разработчики часто имеют натянутые отношения со средствами отладки.

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

Я надеюсь, что это руководство побудит вас использовать отладчики, когда у вас нет ошибки. Информация, получаемая при запуске отладчика, даже если вы не занимаетесь отладкой активно, может «изменить правила игры».