Делай как вGoogle
Скачать 5.77 Mb.
|
410 Глава 19. Critique: инструмент обзора кода в Google Итоги y Доверие и общение — основа процесса обзора кода. Инструмент может дополнить опыт инженера, но не может заменить его. y Важным фактором успеха Critique является тесная интеграция с другими ин- струментами. y Небольшие оптимизации рабочего процесса, такие как добавление явного «на- бора внимания», могут повысить ясность и существенно снизить задержки вы- полнения обзора кода. ГЛАВА 20 Статический анализ Автор: Кейтлин Садовски Редактор: Лиза Кэри Целью статического анализа кода является поиск потенциальных проблем, таких как ошибки, антипаттерны и другие недостатки, которые можно выявить без выполнения программы. Слово «статический» подразумевает анализ именно кода, а не выпол- няющейся программы (которую исследует «динамический» анализ). Статический анализ позволяет обнаружить ошибки на самых ранних этапах разработки, до того как они попадут в продакшен. Например, он может идентифицировать константные выражения, вызывающие переполнение, тесты, которые никогда не запускаются, или строки недопустимого формата в операторах журналирования, которые могут вызвать аварийное завершение при выполнении 1 . Однако статический анализ можно использовать не только для поиска ошибок. Мы в Google с помощью статического анализа внедряем передовые приемы, помогаем поддерживать актуальность кода на уровне современных API и устранить или сократить технический долг. В качестве примеров такого применения статического анализа можно назвать проверку соблю- дения соглашений об именах, выявление попыток использования устаревших API или предложение более простых, но эквивалентных выражений, которые упрощают чтение кода. Кроме того, статический анализ является неотъемлемой частью процесса устаревания API (глава 22). Мы также обнаружили, что проверки кода средствами статического анализа способствуют обучению разработчиков и действительно предотвращают попадание антипаттернов в кодовую базу 2 В этой главе мы обсудим причины эффективности статического анализа и наш опыт его внедрения и реализации 3 Характеристики эффективного статического анализа Исследования статического анализа, направленные на разработку новых и узкоспе- циализированных видов анализа, ведутся уже не одно десятилетие, но внимание 1 http://errorprone.info/bugpatterns 2 Sadowski C. et al. Tricorder: Building a Program Analysis Ecosystem ( https://oreil.ly/9Y-tP ), International Conference on Software Engineering (ICSE), May 2015. 3 Хороший академический справочник по теории статического анализа: Flemming Nielson et al. «Principles of Program Analysis» (Gernamy: Springer, 2004). 412 Глава 20. Статический анализ масштабируемости и удобству использования инструментов статического анализа стало уделяться относительно недавно. Масштабируемость Поскольку размеры ПО постоянно растут, инструменты анализа должны поддер- живать возможность масштабирования, чтобы разработчик мог быстро получать результаты. Инструменты статического анализа в Google должны масштабировать- ся до размеров кодовой базы, насчитывающей миллиарды строк. Для этого они должны поддерживать возможность сегментирования и инкрементального поиска. Вместо анализа больших проектов целиком наши инструменты концентрируются на файлах, вовлеченных в изменение кода, и обычно показывают результаты анализа только для отредактированных файлов или строк. Масштабирование имеет также свои преимущества: поскольку наша база кода очень велика, в ней действительно много мелких ошибок, которые приходится искать. Но кроме возможности работы с большой базой кода мы также должны предоставлять разнообразие видов анализа, которое расширяется благодаря инициативам наших сотрудников. Еще один аспект масштабируемости статического анализа — масштабируемость процесса: инфра- структура статического анализа в Google показывает результаты анализа только тем инженерам, которые имеют непосредственное отношение к этим результатам. Удобство использования Размышляя об удобстве использования инструментов статического анализа, важно учитывать соотношение их цены и выгоды для пользователей. Цена может выра- жаться во времени, которое потратил разработчик, или качестве кода. Исправление предупреждения, полученного от инструмента статического анализа, может при- вести к ошибке. Зачем «исправлять» код, который редко изменяется и нормально работает в продакшене? Например, исправление предупреждения о «мертвом» коде путем добавления его вызова может привести к внезапному запуску непроверен- ного (и, возможно, ошибочного) кода. Выгоды от этих действий неочевидны, а их цена порой слишком высока. По этой причине мы обычно концентрируемся только на вновь появляющихся предупреждениях. Существующие проблемы в работаю- щем коде обычно стоит выделять (и исправлять), только если они действительно важны (проблемы безопасности, исправления серьезных ошибок и т. д.). Благодаря концентрации внимания на новых предупреждениях (или предупреждениях в из- мененных строках) разработчики, просматривающие предупреждения, получают более релевантный контекст проблем. Время разработчика — ценный ресурс! Затраты времени на сортировку отчетов с результатами анализа или исправление выделенных проблем должны компенси- роваться преимуществами конкретного анализа. Если автор анализа может сэко- номить время (например, предложив автоматическое исправление), то он должен сделать это, чтобы уменьшить затраты. Все, что можно исправить автоматически, Ключевые уроки внедрения статического анализа 413 должно исправляться автоматически. Мы также стараемся особо отметить отчеты о проблемах, которые могут оказать отрицательное влияние на качество кода, чтобы разработчики не тратили время на просмотр нерелевантных результатов анализа. Чтобы еще больше уменьшить затраты на обзор результатов статического анализа, мы делаем упор на бесшовную интеграцию инструментов в рабочий процесс раз- работчика. Еще одно преимущество интеграции всех инструментов в один рабочий процесс состоит том, что команда, разрабатывающая инструменты, может обновлять их одновременно с рабочим процессом и кодом, что позволяет развивать инструменты анализа вместе с исходным кодом. Мы считаем, что наши пути и решения, направленные на улучшение масштабируемо- сти и удобства использования средств статического анализа, естественным образом вытекают из нашего внимания к трем основным принципам, которые сформулиро- ваны в виде извлеченных уроков в следующем разделе. Ключевые уроки внедрения статического анализа В ходе внедрения статического анализа мы в Google извлекли три ключевых урока. Рассмотрим их в следующих подразделах. Внимание к удовлетворенности разработчика Мы упомянули некоторые способы, помогающие сэкономить время разработчи- ка и снизить стоимость взаимодействия с инструментами статического анализа. Но также мы следим за тем, насколько хорошо работают наши инструменты. Без этого невозможно решать проблемы. Мы внедряем инструменты анализа только с низким уровнем ложных срабатываний (подробнее об этом ниже), активно со- бираем отзывы разработчиков, использующих результаты статического анализа, и оперативно реагируем на них. Поддержание обратной связи с пользователями инструментов статического анализа и их разработчиками создает эффективный цикл, укрепляющий доверие пользователей друг к другу и к самому инструменту. «Ложноотрицательным» результатом в статическом анализе называют ситуацию, когда фрагмент кода содержит проблему, которую инструмент не замечает. «Ложно- положительный» результат имеет место, когда инструмент ошибочно отмечает код как имеющий проблему. Исследования в области статического анализа традиционно сосредоточены на уменьшении количества ложноотрицательных результатов, но на практике низкий уровень ложноположительных результатов часто имеет не менее решающее значение для разработчиков, которые действительно хотят использовать инструмент — кому захочется продираться через сотни ложных сообщений о про- блемах, чтобы выбрать из них действительно заслуживающие внимания 1 ? 1 Обратите внимание, что для некоторых видов анализа рецензенты готовы мириться с гораздо более высоким уровнем ложноположительных результатов: одним из примеров является анализ безопасности, выявляющий критические проблемы. 414 Глава 20. Статический анализ Кроме того, ключевым аспектом в определении доли ложноположительных ре- зультатов является восприятие. Если инструмент статического анализа генерирует технически обоснованные предупреждения, но ошибочно интерпретируемые поль- зователями как ложноположительные (например, из-за туманных формулировок сообщений), то пользователи будут реагировать на них, как если бы эти пред- упреждения действительно были ложноположительными. Аналогичную реакцию вызывают технически верные, но по большому счету не важные предупреждения. Частоту результатов, ложноположительных с точки зрения пользователя, мы называем «эффективной ложноположительной» частотой. Проблема считается «эффективно ложноположительной», если разработчики не предприняли никаких положительных действий после ее обнаружения. Это означает, что если анализ неверно сообщает о проблеме, но разработчик с готовностью вносит исправление, чтобы улучшить читаемость кода или повысить удобство его сопровождения, то эта проблема не считается эффективно ложноположительной. Например, у нас есть анализ для Java, который отмечает случаи вызова метода contains хеш-таблицы (эк- вивалентного вызову containsValue ), когда на самом деле предполагалось вызвать containsKey , — даже если разработчик действительно намеревался проверить зна- чение, для удобочитаемости предпочтительнее использовать containsValue . Точно так же если анализ сообщает о действительной ошибке, но разработчик не понял смысла сообщения и не предпринял никаких действий, то такой результат считается эффективно ложноположительным. Интеграция статического анализа в рабочий процесс разработчика Мы в Google сделали статический анализ составной частью рабочего процесса, интегрировав его в инструменты обзора кода. В Google весь код, отправляемый в репозиторий, подвергается обзору. На момент отправки кода на проверку разра- ботчики уже готовы к внесению изменений, поэтому исправления, предлагаемые инструментами статического анализа, добавляются без особых проблем. Есть и другие преимущества интеграции статического анализа в обзор кода. После от- правки кода на проверку и до получения отзывов рецензентов у разработчиков появляется время для запуска анализа. Рецензенты со своей стороны заставляют инженеров обратить внимание на предупреждения статического анализа. Кроме того, статический анализ может сэкономить время рецензента, автоматически вы- деляя распространенные проблемы. Трудно найти лучшее время для статического анализа, чем обзор кода 1 Предоставление возможности внести свой вклад В Google работает много экспертов в предметной области, чьи знания могут улучшить создаваемый код. Статический анализ дает этим экспертам возможность распростра- 1 Далее в этой главе мы поговорим о дополнительных точках интеграции с процессами ре- дактирования и просмотра кода. Tricorder: платформа статического анализа в Google 415 нения своего опыта через создание новых инструментов анализа или разработку отдельных проверок в рамках выбранного инструмента. Например, эксперты, хорошо знающие конкретный тип конфигурационных фай- лов, могут написать инструмент для проверки определений свойств в этих файлах. В свою очередь, разработчики, обнаружив ошибку, могут предотвратить ее повторное появление где-либо еще в кодовой базе. Для всех специалистов мы создаем легко подключаемую экосистему статического анализа. Мы стремимся разрабатывать простые API, которые могут использоваться всеми инженерами в Google, а не только специалистами по анализу или языку. Например, Refaster 1 позволяет на- писать анализатор, представив фрагменты кода, которые демонстрируют, как код выглядит до преобразования этим анализатором и как он должен выглядеть после преобразования. Tricorder: платформа статического анализа в Google Фундаментом статического анализа в Google служит платформа Tricorder 2 . Она появилась в результате нескольких неудачных попыток интегрировать статический анализ в рабочий процесс разработчика 3 . Ключевым отличием Tricorder от предыду- щих попыток является наше неуклонное стремление дать своим пользователям только ценные результаты. Tricorder тесно интегрирована с Critique — основным инструментом обзора кода в Google. Предупреждения, генерируемые Tricorder, ото- бражаются в средстве просмотра различий в Critique в виде серых панелей с ком- ментариями (рис. 20.1). Для масштабирования Tricorder использует архитектуру микросервисов. Она от- правляет серверам анализа запросы вместе с метаданными об изменениях в коде, а серверы используют эти метаданные для получения версий файлов с изменениями из файловой системы FUSE и входных и выходных данных из кешей системы сбор- ки. Затем серверы запускают каждый отдельный инструмент анализа, записывают результаты в хранилище, а самые последние результаты для каждой категории за- тем отображаются в Critique. Поскольку иногда для выполнения анализа требуется несколько минут, серверы анализа также сообщают информацию о его состоянии, чтобы авторы изменений и рецензенты могли видеть, что инструменты запущены 1 Wasserman L. Scalable, Example-Based Refactorings with Refaster ( https://oreil.ly/XUkFp ). Workshop on Refactoring Tools, 2013. 2 Sadowski C., Gogh J. van, Jaspan C., S öderberg E., Winter C. Tricorder: Building a Program Analysis Ecosystem ( https://oreil.ly/mJXTD ), International Conference on Software Engineering (ICSE), May 2015. 3 Sadowski C., Aftandilian E., Eagle A., Miller-Cushon L., Jaspan C. Lessons from Building Static Analysis Tools at Google. Communications of the ACM, 61 No. 4 (April 2018): 58–66, https:// cacm.acm.org/magazines/2018/4/226371-lessons-from-building-static-analysis-tools-at-google/ fulltext 416 Глава 20. Статический анализ или уже завершили анализ. Tricorder анализирует более 50 000 изменений в день и часто выполняет несколько анализов в секунду. Рис. 20.1. Средство просмотра различий в Critique, отображающее панели с предупреждениями, сгенерированными платформой статического анализа Tricorder Разработчики в Google пишут анализы для Tricorder (так называемые «анализато- ры») или добавляют отдельные «проверки» в существующие анализы. Есть четыре критерия, которыми должны руководствоваться разработчики при внедрении новых проверок в Tricorder: Понятность Текст сообщения должен быть понятен инженеру. Практичность и простота исправления Исправление проблемы может потребовать много времени, размышлений или усилий, поэтому результат анализа должен включать описание, поясняющее, как можно исправить проблему. Не более 10 % эффективно ложноположительных результатов Разработчики должны быть уверены, что сообщение указывает на реальную про- блему как минимум в 90 % случаев ( https://oreil.ly/ARSzt ). Существенное влияние на качество кода Отмеченные проблемы могут не влиять на правильность кода, но разработчики должны серьезно отнестись к ним и со всей ответственностью подойти к их ис- правлению. Анализаторы Tricorder поддерживают более 30 языков и различные виды анализа. В Tricorder есть более 100 анализаторов, большинство из которых создано за преде- лами команды Tricorder. Семь из этих анализаторов сами имеют расширяемую ар- хитектуру и включают сотни дополнительных проверок, созданных разработчиками Google. Общая частота эффективно ложноположительных результатов находится на уровне чуть ниже 5 %. Tricorder: платформа статического анализа в Google 417 Интегрированные инструменты В Tricorder интегрировано множество инструментов статического анализа. Error Prone ( http://errorprone.info ) и clang-tidy ( https://oreil.ly/DAMiv ) расширяют ком- пилятор, добавляя проверки антипаттернов AST для Java и C++ соответственно. Эти антипаттерны могут представлять настоящие ошибки. Например, взгляните на следующий фрагмент кода, выполняющий хеширование поля f типа long : result = 31 * result + (int) (f ^ (f >>> 32)); Теперь представьте, что f имеет тип int . Код все равно будет компилироваться, но сдвиг вправо на 32 позиции является пустой операцией для этого типа, и в результате будет выполнена операция исключающего ИЛИ (XOR) поля f с самим собой, никак не влияющая на получаемое значение. Мы исправили 31 появление этой ошибки в ко- довой базе Google, включив проверку как ошибку компилятора в Error Prone. И таких примеров очень много ( https://errorprone.info/bugpatterns ). Устранение антипаттернов AST также может способствовать улучшению удобочитаемости кода, например за счет удаления избыточных вызовов метода .get() интеллектуального указателя. Другие анализаторы отражают взаимосвязи между разрозненными файлами в кор- пусе анализа. Анализатор удаленных артефактов Deleted Artifact Analyzer пред- упреждает об удалении исходного файла, на который есть ссылки в других местах базы кода, не относящихся к анализируемому коду (например, в документации, хра- нящейся в репозитории). IfThisThenThat позволяет разработчикам указать, что два разных файла должны изменяться вместе (и предупреждает, обнаружив изменение только в одном из них). Анализатор Chrome Finch работает с файлами конфигурации, предназначенными для A/B-экспериментов в Chrome, выявляя распространенные проблемы, включая отсутствие необходимых разрешений на запуск эксперимента или перекрестные конфликты с другими текущими экспериментами, затрагиваю- щими ту же популяцию. Анализатор Finch выполняет RPC в других службах, чтобы предоставить информацию об удаленных процедурах. Некоторые анализаторы работают не только с исходным кодом, но и с другими артефактами, созданными этим исходным кодом. Во многих проектах включена функция проверки размера двоичного файла, которая предупреждает, когда изме- нение существенно влияет на этот размер. Почти все анализаторы являются внутрипроцедурными, то есть их результаты основаны на анализе кода внутри процедуры (функции). Композиционные и ин- крементальные методы межпроцедурного анализа технически осуществимы, но тре- буют дополнительных вложений в инфраструктуру (например, в анализ и хранение сводных данных о методах во время работы анализаторов). |