Делай как вGoogle
Скачать 5.77 Mb.
|
Интегрированные каналы обратной связи Как упоминалось выше, обратная связь между пользователями и разработчиками ана- лиза чрезвычайно важна для удовлетворенности разработчиков. В Tricorder мы даем 418 Глава 20. Статический анализ пользователям возможность щелкнуть на кнопке Not useful (Бесполезно) в результатах анализа, чтобы сформировать сообщение об ошибке анализа с описанием, почему анализ был расценен как бесполезный, и отправить это сообщение непосредственно автору анализатора. Рецензенты кода также могут попросить авторов изменений исправить выявленные проблемы, щелкнув на кнопке Please fix (Исправьте) в ре- зультатах анализа. Команда Tricorder внимательно наблюдает, как часто рецензенты просят исправить проблемы, выявленные в ходе анализа, и отключает анализаторы, если они не помогают в решении проблем или имеют высокий показатель «бесполез- ности». Организация и настройка обратной связи потребовали много усилий, но эти трудозатраты не раз приносили дивиденды в виде улучшения результатов анализа и взаимодействия с пользователями — до того как мы установили четкие каналы обратной связи, многие разработчики просто игнорировали результаты анализа, которые казались им непонятными. Иногда исправить недостатки в анализаторе было довольно просто — нужно было всего лишь изменить текст сообщения! Например, однажды мы развернули в Error Prone проверку, которая отмечала вызовы printf -подобной функции в Guava со слишком большим количеством аргументов. Эта функция принимала только %s и никакие другие спецификаторы printf . Команда Error Prone еженедельно получала сообщения «Бесполезно», в которых утверждалось, что анализ дает неправильные результаты, потому что количество спецификаторов формата соответствовало коли- честву аргументов, но пользователи пытались передавать спецификаторы, отличные от %s . После того как группа изменила диагностический текст, прямо указав, что функция принимает только спецификатор %s , приток отчетов об ошибках прекра- тился. Уточняющее сообщение, генерируемое инструментом анализа, пояснило, что именно неправильно, почему и как исправить проблему. Такие уточнения особенно важны для разработчиков, которые узнают что-то новое, читая сообщения. Предлагаемые исправления Проверки в Tricorder также могут предлагать варианты исправления проблемы, когда это возможно (рис. 20.2). Рис. 20.2. Пример результатов анализа с предлагаемым исправлением в Critique Автоматически предлагаемые исправления служат дополнительным источником информации, когда сообщение оказывается недостаточно ясным, и, как упоминалось Tricorder: платформа статического анализа в Google 419 выше, сокращают затраты на устранение проблем, выявленных статическим анали- зом. Исправления можно применять непосредственно из Critique или с помощью инструмента командной строки. Но не все анализаторы предлагают исправления. Мы придерживаемся мнения, что проблемы стиля должны устраняться инструментами, которые автоматически форматируют файлы с исходным кодом. В Google есть свои руководства по стилю для каждого языка, где перечислены правила форматирова- ния, поэтому сообщения с описанием ошибок форматирования не стоят внимания рецензента. Рецензенты щелкают на кнопке Please Fix (Исправьте) несколько тысяч раз в день, авторы применяют автоматически предлагаемые исправления примерно 3000 раз в день, и анализаторы Tricorder получают около 250 щелчков Not useful (Бес- полезно) в день. Настройки для конкретных проектов Создав платформу, заслужившую доверие пользователей отображением только максимально достоверных результатов, мы добавили в нее возможность запуска дополнительных «необязательных» анализаторов в конкретных проектах. При- мером такого дополнительного анализатора может служить Proto Best Practices. Он выявляет потенциально опасные изменения в формате данных в Protocol Buffers ( https://developers.google.com/protocol-buffers ) — не зависящем от языка формате се- риализации данных, разработанном в Google. Эти изменения могут быть опасными, только когда сериализованные данные хранятся, например, в журналах сервера. Проверка Protocol Buffers не требуется в проектах, не хранящих сериализованные данные. Мы также добавили возможность настройки существующих анализаторов, хотя и ограниченную, и многие проверки применяются по умолчанию единообразно для всей кодовой базы. Некоторые анализаторы начинали свой путь как дополнительные, улучшались на основе отзывов, расширяли круг своих пользователей, а затем переходили в кате- горию анализаторов, включенных по умолчанию, как только заслуживали доверие пользователей. Например, у нас есть анализатор, который предлагает исправления, улучшающие удобочитаемость кода на Java, но обычно не влияющие на его поведение. Пользователи Tricorder сначала волновались, что этот анализатор будет генерировать слишком много сообщений, но в итоге в них проснулось желание получать больше результатов от такого анализа. Ключ к успешной настройке платформы статического анализа — сосредоточиться на настройке на уровне проекта, а не на уровне пользователя. Настройка на уровне проекта гарантирует, что все члены команды получат единообразное представление результатов анализа для своего проекта, и предотвратит ситуации, когда один раз- работчик пытается исправить проблему, а другой порождает ее. Изначально платформа Tricorder поддерживала набор относительно простых средств проверки стиля и отображала результаты в Critique, а Critique предоставлял возмож- ность настройки уровня достоверности отображаемых результатов и подавления 420 Глава 20. Статический анализ результатов конкретных анализаторов. Потом мы удалили из Critique эти настройки и сразу начали получать от пользователей жалобы на раздражающие результаты анализа. Вместо того чтобы вернуть возможность настройки, мы предложили пользователям рассказать, что их раздражает, и обнаружили в анализаторах стиля множество ошибок. Например, анализатор стиля для C++ при использовании для Objective-C давал неверные и бесполезные результаты. Мы исправили эту проблему, изменив инфраструктуру анализа стиля. Анализатор HTML имел чрезвычайно высо- кую ложноположительную частоту и очень низкий уровень полезного сигнала, из-за чего обычно отключался разработчиками, пишущими разметку HTML. Поскольку этот инструмент оказался практически бесполезным, мы просто отключили его. Так возможность настройки на уровне пользователя привела к появлению множества скрытых ошибок и подавлению обратной связи. Предварительные проверки Кроме обзора кода в Google есть другие точки интеграции статического анализа в рабочий процесс. Поскольку разработчики могут игнорировать предупреждения статического анализа, отображаемые при обзоре кода, в Google была реализована дополнительная возможность добавить анализ, блокирующий попытку фиксации изменения в репозитории, который мы называем предварительной проверкой, или проверкой перед отправкой. К числу предварительных проверок относятся очень простые настраиваемые внутренние проверки содержимого или метадан- ных изменения, такие как проверка присутствия в сообщении фиксации текста «DO NOT SUBMIT» (НЕ ОТПРАВЛЯТЬ) или подключения тестовых файлов в соответствующих файлах с кодом. Команды также могут указать, какие наборы тестов должны выполняться или какие категории проблем не должны выявлять- ся системой Tricorder. В процессе предварительной проверки также оценивается правильность формата кода. Предварительные проверки обычно выполняются при отправке изменения для обзора, а затем снова во время фиксации, но также могут запускаться по запросу между этими двумя точками. Подробнее о предварительных проверках в главе 23. Некоторые команды написали свои предварительные проверки. Они дополняют базовый набор проверок, позволяют реализовать передовые практики и добавляют новые виды анализа для конкретных проектов. Благодаря им новые проекты могут руководствоваться более строгими принципами, чем, например, проекты с большим количеством устаревшего кода. Но предварительные проверки для конкретных ко- манд могут усложнить процесс крупномасштабных изменений (см. главу 22), поэтому некоторые из них пропускаются для изменений с текстом«CLEANUP =» в описании. Интеграция с компилятором Блокировка попыток фиксации с помощью статического анализа — это, конечно, хорошо, но лучше было бы уведомлять разработчиков о проблемах еще раньше. Tricorder: платформа статического анализа в Google 421 Когда это возможно, мы стараемся добавить статический анализ в компилятор. Прерывание сборки — это предупреждение, которое невозможно игнорировать, но во многих случаях его нельзя организовать. Однако некоторые анализы имеют механический характер и не дают эффективно ложноположительных результатов. Примером могут служить проверки «ERROR» в Error Prone ( https://errorprone.info/ bugpatterns ). Они включены в компилятор Java, который используется в Google, и предотвращают повторное появление ошибок в нашей кодовой базе. Проверки в компиляторе должны выполняться быстро, чтобы не замедлять сборку, и отвечать трем критериям (аналогичные критерии существуют для компилятора C++): y практичность и простота исправления (по возможности сообщение об ошибке должно включать предложение по исправлению, которое можно применить автоматически); y отсутствие эффективно ложноположительных результатов (анализ никогда не должен останавливать сборку правильного кода); y сообщение только о проблемах, влияющих на правильность кода, но не на стиль или использование передовых практик. Включать новую проверку нужно после удаления из базы кода всех проблем, выяв- ляемых ею, чтобы сборка существующих проектов не нарушилась из-за эволюции компилятора. Это также означает, что новая проверка, интегрируемая в компилятор, должна быть достаточно ценной, чтобы взяться за работу по исправлению всех вы- являемых проблем. В Google имеется своя кластерная инфраструктура для запуска различных компиляторов (таких, как clang и javac) для всей кодовой базы, действую- щая как MapReduce. Когда компиляторы запускаются в режиме MapReduce, проверки статического анализа должны сами исправлять обнаруженные проблемы, чтобы авто- матизировать чистку. После подготовки и тестирования изменения с исправлениями ко всей кодовой базе мы фиксируем его и устраняем выявленные проблемы. Затем мы включаем проверку в компиляторе, чтобы никакие новые проявления проблемы не смогли проникнуть в кодовую базу и прерывать процедуру сборки. Прерывания сборки обнаруживаются после фиксации изменений нашей системой непрерывной интеграции или перед их фиксацией с помощью предварительных проверок (как описывалось выше). Мы также стараемся никогда не генерировать сообщения в форме предупреждений компилятора, потому что, как показал опыт, разработчики часто игнорируют пред- упреждения. Поэтому мы включаем проверку в компилятор как ошибку (и прерываем сборку) или не показываем ее в выводе компилятора. Поскольку во всей кодовой базе используются одни и те же флаги компилятора, это решение принято глобально. Проверки, которые не должны прерывать сборку, либо подавляются, либо отобра- жаются при проверке кода (например, в Tricorder). Эта политика применяется не ко всем языкам в Google, но для наиболее часто используемых языков она реализована. Компиляторы Java и C++ не отображают предупреждения компилятора. А в Go являются ошибками определенные сообщения, которые в других языках считаются 422 Глава 20. Статический анализ предупреждениями (например, неиспользуемые переменные или импортирование нетребуемых пакетов). Анализ в процессе редактирования и просмотра кода Еще одна потенциальная точка интеграции статического анализа — IDE. Однако для использования в IDE анализ должен выполняться очень быстро (не более одной секунды, желательно — менее 100 мс), поэтому некоторые инструменты не подходят для интеграции с IDE. Кроме того, существует проблема обеспечения идентичности результатов одного и того же анализа в нескольких IDE. Мы также заметили, что популярность IDE может расти и падать (мы не требуем использовать какую-то единую IDE), поэтому интеграция с IDE имеет более беспорядочной характер, чем интеграция с процессом обзора кода. Процесс обзора — самое подходящее место для отображения результатов анализа. Анализ может учитывать весь контекст изменения, но некоторые анализы могут ошибаться при работе с неполным кодом (например, при анализе «мертвого» кода функция реализуется до добавления ее вызовов). Ото- бражение результатов анализа в обзоре кода также вынуждает авторов кода убедить рецензентов игнорировать результаты анализа, если это необходимо. Тем не менее в IDE тоже можно отобразить результаты статического анализа. Мы стараемся отображать только самые последние предупреждения статического анализа, но иногда разработчики хотят видеть результаты анализа для всей кодовой базы при ее просмотре (например, при анализе безопасности). Специализирован- ные группы безопасности в Google хотят иметь целостное представление обо всех проявлениях проблем. Кроме того, разработчики любят просматривать результа- ты анализа кодовой базы при планировании чистки. Таким образом, в некоторых случаях инструмент просмотра кода является более чем подходящим местом для отображения результатов анализа. Заключение Статический анализ — отличный инструмент для улучшения кодовой базы и раннего обнаружения ошибок, который позволяет в более дорогостоящих процессах (таких, как проверка и тестирование кода вручную) сосредоточиться на проблемах, которые невозможно выявить автоматически. Улучшив масштабируемость и удобство ис- пользования инфраструктуры статического анализа, мы превратили статический анализ в эффективный компонент разработки ПО в Google. Итоги y Внимание к удовлетворенности разработчика. Мы много трудились над созданием каналов обратной связи между пользователями и авторами анализа и продол- жаем активно развивать процесс анализа, чтобы уменьшить количество ложных результатов. Итоги 423 y Интеграция статического анализа в рабочий процесс разработчика. Главной точкой интеграции статического анализа в Google является процесс обзора кода, в котором инструменты анализа предлагают возможные исправления и распреде- ляют внимание рецензентов. Также мы интегрировали анализ в дополнительных точках (компиляторе, системе фиксации кода в репозитории, IDE и средствах просмотра кода). y Предоставление возможности внести свой вклад. Мы масштабируем свою работу, используя опыт экспертов для создания и обслуживая инструментов и платформы анализа. Разработчики постоянно добавляют новые виды анализа и проверки, которые делают их работу проще, а нашу кодовую базу — лучше. ГЛАВА 21 Управление зависимостями Автор: Титус Винтер Редактор: Лиза Кэри Управление зависимостями — коллекциями сторонних библиотек, пакетов и зави- симостей — является одной из наиболее сложных задач в программной инженерии. Управление зависимостями фокусируется на вопросах: «Как обновлять версии внешних зависимостей?», «Как правильно их описывать?», «Какие типы изменений допустимы или ожидаемы в зависимостях?», «Как не ошибиться, принимая решение о создании зависимости от кода, разрабатываемого другими организациями?» Наиболее близкой к задаче управления зависимостями является задача управления версиями исходного кода. Обе связаны с управлением исходным кодом. Управление версиями отвечает на простые вопросы: «Откуда брать исходный код?», «Как до- бавить исходный код в сборку?» После признания ценности разработки в главной ветви большинство повседневных вопросов управления версиями сводится к одному: «У меня есть новый код, в какой каталог его следует поместить?» Управление зависимостями добавляет к этим вопросам дополнительные сложности, имеющие отношение к масштабированию. В задаче управления исходным кодом на основе главной ветви вы знаете, что после внесения изменений запустите тесты, чтобы убедиться, что не повредили существующий код. Суть этой идеи проста: вы работаете с общей базой кода, имеете представление о том, как используется тот или иной код, и можете запустить сборку и тесты. Управление зависимостями фокусируется на проблемах, которые возникают, когда изменения вносятся вне вашей организации и вам неизвестно, что именно и как изменилось. Поскольку внешние проекты часто не согласовывают с вами свои изменения, эти изменения могут нарушить ваш про- цесс сборки и привести к сбою ваши тесты. Как справиться с этим? Отказаться от внешних зависимостей? Потребовать от внешних проектов согласовывать изменения с вами? Когда следует обновить зависимость до новой версии? Еще сложнее импортировать зависимости от целой сети внешних проектов. Как толь- ко появляется несколько зависимостей, легко оказаться в ситуации, когда какие-то две зависимости начинают конфликтовать. Обычно это происходит из-за того, что одна зависимость предъявляет некоторое требование 1 , а другая с ним несовместима. 1 Это может быть любое требование: версия языка, версия низкоуровневой библиотеки, версия оборудования, операционная система, флаги компилятора, версия компилятора и т. д. Почему управлять зависимостями так сложно? 425 Простые решения, ориентированные на управление единственной внешней зависи- мостью, обычно не учитывают реалии управления обширной сетью зависимостей. Большую часть этой главы мы посвятим обсуждению конфликтующих требований. Управление исходным кодом и управление зависимостями — это родственные задачи, отличающиеся только ответом на вопрос: «Контролирует ли организация разработку/ обновление/управление в используемом проекте?» Например, если каждая команда в компании имеет свои репозитории, преследует свои цели, использует свои методы разработки и организации взаимодействий и по-своему управляет исходным кодом, то такой компании придется решать скорее задачу управления зависимостями, чем управления исходным кодом. В свою очередь, большая организация с единым репо- зиторием (монолитным и, возможно, виртуальным) может значительно расширить свои масштабы с помощью политик управления исходным кодом — именно этот подход используется в Google. Проекты с открытым исходным кодом, безусловно, считаются отдельными организациями, в которых управление взаимозависимостя- ми между несвязанными и не обязательно сотрудничающими проектами относится к задаче управления зависимостями. Наш самый действенный совет по этой теме: если возможно, отдавайте предпочтение управлению исходным кодом, а не управле- нию зависимостями. Старайтесь использовать более широкое определение термина «организация» (например, вся компания, а не одна команда). Задача управления исходным кодом решается намного проще, чем задача управления зависимостями. Модель ПО с открытым исходным кодом продолжает развиваться и проникать в но- вые области, и графы зависимостей во многих популярных проектах продолжают расширяться со временем, поэтому управление зависимостями становится, пожалуй, самой важной задачей в программной инженерии. Софтверные компании больше не острова в безбрежном океане, связанные одним или двумя уровнями API. Совре- менное ПО опирается на возвышающиеся столпы зависимостей, но, создавая такие столпы, мы еще не научились сохранять их устойчивость и надежность. В этой главе мы рассмотрим конкретные сложности управления зависимостями, ис- следуем решения (общие и новые) и их ограничения, а также посмотрим на реалии работы с зависимостями, в том числе в Google. Мы много думали над этой проблемой и имеем большой опыт в вопросах рефакторинга и сопровождения, который пока- зывает практические недостатки существующих подходов. И должны признаться, что у нас нет свидетельств существования решений, хорошо работающих в больших организациях. В какой-то степени эта глава представляет собой краткое описание подходов, которые не работают (или, по крайней мере, могут не работать в больших компаниях), и подходов, которые, по нашему мнению, имеют определенный потен- циал. Если бы у нас было больше ответов, мы не назвали бы эту проблему одной из самых важных в программной инженерии. |