Как исследовать незнакомый код. Часть 1

Популярное утверждение о коде состоит в том, что его читают в десять раз чаще, чем пишут. Это обычно используется в качестве аргумента в пользу привередливого кодирования: вы будете часто видеть свой код, поэтому потратьте дополнительное время на качество. Рецензируемых доказательств этого утверждения немного, но все, что мне удалось найти, показывает соотношение 10:1 как крайне консервативное. Фактическое соотношение, возможно, ближе к 100 к 1. И чем дольше срок службы продукта, тем выше это соотношение.

В свете этой информации кажется, что мы недостаточно инвестируем в умение понимать код. Книги и учебные пособия обычно фокусируются на искусстве написания кода, на способности программиста теоретизировать, писать и модифицировать код эффективным и удобочитаемым способом, а не на гораздо более распространенной деятельности по чтению и интерпретации уже существующего кода. Это правда, что чтение кода — это навык, который следует за его написанием: если вы умеете писать на Python, само собой разумеется, что вы можете его понять. Но чтение кода — это тоже навык сам по себе.

Новые разработчики иногда считают, что если они не тратят большую часть своего времени на добавление нового кода в проект, они не работают продуктивно. Такое отношение действительно может сдерживать вас. Эффективное кодирование требует как контекста, так и уверенности: вам нужно понимать среду, в которой будет жить ваш код, и быть уверенным, что ваша работа приносит пользу. Первые 80-95% времени, которое вы тратите на задачу, должны быть потрачены на чтение кода и других форм документации. Иногда это даже 100% — в процессе изучения существующего кода вы можете узнать достаточно, чтобы сказать «эта функция уже существует, мы просто забыли о ней» или «это принесет больше вреда, чем пользы».

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

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

Установите полезные плагины

Ваша IDE — бесценный инструмент для понимания кода. Такие редакторы, как Visual Studio, VS Code, Eclipse и IntelliJ IDEA, живут и умирают благодаря своим возможностям синтаксического анализа кода и размеру библиотек подключаемых модулей. С каким бы языком, фреймворком или облачным сервисом вы ни работали, вам потребуется несколько минут, чтобы установить соответствующие плагины. Это поможет вам ориентироваться в кодовой базе и быстрее замечать проблемы. Официальные сторонние плагины лучше, если вы можете их найти, но популярные плагины, поддерживаемые сообществом, также могут быть отличными.

Ищите следующие функции:
  • Подсветка синтаксиса: ключевые слова, имена классов/методов/полей/переменных и скобки отображаются разными цветами для облегчения понимания.
  • Автоформатирование: изменяет пробелы, длину строки и другие элементы стиля, чтобы сделать их более читабельными и последовательными. Обычно вы можете настроить это так, чтобы это происходило с помощью сочетания клавиш или каждый раз, когда вы сохраняете файл.
  • Статический анализ: предупреждает вас о проблемах в вашем коде, фактически не запуская его. Например, если вы неправильно написали имя переменной или использовали неправильный тип кавычек, инструменты статического анализа, встроенные в вашу IDE или многофункциональный языковой плагин, подчеркнут это красным цветом.
  • Контекстная навигация: предоставляет такие пункты меню, как «Перейти к определению», «Просмотреть реализации» и «Просмотреть ссылки», когда вы открываете контекстное меню (щелчок правой кнопкой мыши) на идентификаторе.
  • Рефакторинг: автоматизирует общие рефакторинги, такие как извлечение логики в метод, изменение параметров метода или переименование переменной.
  • Подсказка по коду: показывает информацию (например, типы, параметры и рукописную документацию) о классе/методе/поле, когда вы наводите на него курсор.
  • Средство запуска тестов: предоставляет пользовательский интерфейс для запуска модульных и интеграционных тестов и сообщает о результатах.
  • Отладчик: позволяет вам устанавливать точки останова в вашем коде, чтобы вы могли проходить определенный процесс по одной строке за раз и проверять значения в области видимости.
  • Интеграция с контролем версий: помогает синхронизировать и объединять код с остальной частью вашей команды. Также предоставляет информацию об авторе и дате последнего редактирования каждой строки кода.

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

Прочтите код как минимум дважды

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

При первом чтении постарайтесь получить общую картину, просматривая и создавая схему в уме (или на бумаге, если это помогает). Ваша цель — суметь обобщить то, что делает код. Если у вас под рукой есть резиновая уточка, сейчас самое время начать с ней разговаривать. Объясните общую цель кода в основных терминах. Если вы немного затуманите некоторые части, это привлечет к ним внимание.

Второе чтение больше касается деталей. Убедитесь, что вы понимаете каждую строку кода или, по крайней мере, имеете теорию об этом. Обратите особое внимание на внешние эффекты. Какие методы называются? Какие общие ценности обновляются? Каковы возвращаемые значения? Потратьте время на изучение каждого из них, чтобы вы могли понять всю логику игры, даже если она находится за пределами изучаемого вами кода. Перейдите к другим методам (ctrl + щелчок в большинстве IDE), наведите указатель мыши на библиотечные методы, чтобы прочитать документацию, откройте вкладку браузера, чтобы проверить, что означает конкретный фрагмент синтаксиса. Найдите операторы import, include или using в верхней части файла, чтобы узнать, какие пространства имен и библиотеки используются. Когда вы закончите, у вас будут теории о возможном поведении, пограничных случаях и условиях отказа в коде.

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

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

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

Рефакторинг имен локальных переменных и методов

Иногда часть кода настолько расплывчата или вводит в заблуждение, что ее трудно осмыслить. Один практически безрисковый способ добиться прогресса — переименовать локальные переменные и частные методы, чтобы более точно описать, что они делают. Эти типы изменений не повлияют ни на что за пределами файла, с которым вы работаете, и не вызовут логических ошибок, если вы будете осторожны, чтобы избежать конфликтов имен. Если возможно, используйте инструменты рефакторинга вашей IDE (а не поиск и замену текста), чтобы вы могли переименовывать что-либо везде, где оно используется, одним щелчком мыши.

Например, рассмотрим следующий фрагмент JavaScript:
function ib(a, fn) {
  return (a || []).reduce((o, i) => {
    o[fn(i)] = i;
    return o;
  }, {});
}

Его очень трудно читать, и название ib бесполезно, чтобы помочь вам понять, что он делает. Однако вы можете сделать некоторые выводы об этом:
  • Поскольку для a вызывается метод reduce (и он возвращается к пустому массиву), a должен быть типом массива.
  • Аргумент обратного вызова i будет элементом этого массива.
  • Второй аргумент для сокращения, литерал пустого объекта {}, говорит нам, что аргумент обратного вызова o является словарем (объектом).
Итак, небольшое переименование приводит нас сюда:
function ib(array, fn) {
  return (array || []).reduce((dict, element) => {
    dict[fn(element)] = element;
    return dict;
  }, {});
}

Теперь вы можете видеть, что fn используется для превращения элемента массива в ключ словаря. И это раскрывает цель функции ib: преобразовать массив в словарь, используя пользовательский обратный вызов для определения ключа, который индексирует каждый элемент. Вы можете переименовать fn в getKey для большей ясности, а ib следует назвать indexBy или toDictionary или что-то в этом роде. Почти все было бы лучше, чем то, что сейчас называется.

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

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

Похожие публикации

Тут ничего нет

Нет комментариев