Делай как вGoogle
Скачать 5.77 Mb.
|
466 Глава 22. Крупномасштабные изменения Авторизация Мы просим потенциальных авторов представить короткий документ, объясняющий причину предлагаемого изменения, его предполагаемое влияние на кодовую базу (на сколько сегментов можно разделить большое изменение) и содержащий ответы на любые вопросы, которые могут возникнуть у потенциальных рецензентов. Этот процесс заставляет авторов подумать о том, как они будут описывать изменение другому инженеру в форме сборника ответов на часто задаваемые вопросы. Авторы также получают «предметный обзор» от владельцев реорганизуемого API. Этот документ-предложение затем пересылается по электронной почте комитету специалистов, в которую входит около десятка человек. После обсуждения коми- тет дает рекомендации о том, как двигаться дальше. Например, одним из наиболее распространенных изменений, вносимых комитетом, является направление всех обзоров кода для крупномасштабного изменения одному «глобальному утверж- дающему лицу». Многие авторы, впервые предпринимающие попытку внести крупномасштабные изменения, склонны думать, что владельцы проектов должны все проверить, но для большинства механических изменений дешевле иметь одного эксперта, который понимает природу изменения и строит автоматизацию для его анализа. После утверждения изменения автор может продолжить отправку своего изменения. По традиции комитет очень либерально подходит к одобрению 1 и часто утверждает не только конкретное изменение, но и широкий набор связанных изменений. Члены комитета могут по своему усмотрению ускорить очевидные изменения без полного обсуждения. Цель этого процесса — обеспечить надзор и управление и освободить от этой ответ- ственности авторов крупномасштабных изменений. Комитет также наделен полно- мочиями органа по рассмотрению проблем или конфликтов в крупномасштабном изменении: владельцы проектов, не согласные с изменением, могут обратиться к этой группе, чтобы уладить конфликт. Но на практике такое случается редко. Создание изменения Получив одобрение, автор крупномасштабного изменения приступает к редакти- рованию кода. Иногда правки могут быть сгенерированы в виде одного большого и глобального изменения, которое впоследствии будет разделено на множество более мелких независимых частей. Обычно размер изменения слишком велик, чтобы уместиться в одно глобальное изменение из-за технических ограничений базовой VCS. 1 Комитет категорически отклоняет только изменения, признанные опасными, например преобразование всех экземпляров NULL в nullptr , или слишком малозначимые, например изменение орфографии с британского английского на американский английский или наобо- рот. С ростом нашего опыта в отношении таких изменений стоимость крупномасштабных изменений снизилась и снизился порог утверждения. Процесс крупномасштабных изменений 467 Процесс генерирования изменений должен быть максимально автоматизирован, чтобы родительское изменение можно было повторить, если пользователи вер- нутся к старому варианту использования 1 или возникнут конфликты при слиянии измененного кода. В редких случаях, когда технические инструменты не способны сгенерировать глобальное изменение, мы распределяем генерацию изменений между инженерами (см. врезку «Пример: операция RoseHub» выше). Это требует намного больше трудозатрат, но позволяет вносить глобальные изменения гораздо быстрее, что особенно важно для приложений, чувствительных ко времени. Напомним, что мы оптимизируем нашу кодовую базу для удобства чтения, поэтому независимо от инструмента, генерирующего изменения, мы хотим, чтобы они полу- чились максимально похожими на изменения, созданные человеком. Это требование приводит к необходимости следования руководствам по стилю и использования средств автоматического форматирования (глава 8) 2 Деление на части и отправка Сгенерировав глобальное изменение, автор запускает Rosie. Этот инструмент де- лит большое изменение на части по границам проектов с учетом правил владения, которые можно отправлять атомарно. Затем каждая часть изменения независимо проходит через конвейер тестирования, обзора и отправки. Система Rosie может активно пользоваться другими частями инфраструктуры разработки в Google, по- этому она ограничивает количество одновременно обрабатываемых частей любого заданного крупномасштабного изменения, выполняется с низким приоритетом и взаимодействует с остальной инфраструктурой, определяя, какую нагрузку она может создать для общей инфраструктуры тестирования. Ниже мы подробнее поговорим о конкретном процессе тестирования, обзора и от- правки для каждой части. Тестирование Каждая независимая часть изменения тестируется с помощью TAP — платформы непрерывной интеграции в Google. Мы запускаем все тесты, зависящие от файлов в данном изменении прямо или опосредованно, что часто создает высокую нагрузку на систему непрерывной интеграции. Этот процесс может показаться слишком дорогостоящим с точки зрения вычис- лительных ресурсов, но на практике подавляющее большинство частей затрагивает менее тысячи тестов из миллионов в нашей кодовой базе. Те части, которые затраги- вают большее количество тестов, мы можем сгруппировать: сначала объединить все затронутые тесты для всех частей, а затем для каждой отдельной части выполнить 1 Это может случиться по разным причинам: копирование кода из существующих примеров, фиксация изменений, находившихся в разработке в течение некоторого времени, или просто зависимость от старых привычек. 2 Фактически именно по этой причине были начаты работы над clang-format для C++. 468 Глава 22. Крупномасштабные изменения только пересечение множества затронутых ею тестов с множеством тестов, по- терпевших неудачу в первом прогоне. Большинство таких объединений вызывают выполнение почти всех тестов в кодовой базе, поэтому добавление дополнительных изменений в этот пакет сегментов практически на приводит к увеличению затрат. СКОТ И ДОМАШНИЕ ЛЮБИМЦЫ Мы часто используем аналогию «скот и домашние любимцы», ссылаясь на отдельные машины в распределенной вычислительной среде, но эту же аналогию можно применить к изменениям в кодовой базе. В Google, как и в большинстве организаций, большинство изменений в кодовую базу вруч- ную вносят отдельные инженеры, работающие над конкретными функциями или исправ- лениями ошибок. Инженеры могут потратить дни или недели на создание, тестирование и анализ одного изменения. Они близко знакомы с изменением и гордятся тем, что оно наконец попадет в основной репозиторий. Создание такого изменения сродни владению и воспитанию домашнего любимца. Для эффективного обращения с крупномасштабными изменениями, напротив, требуются высокая степень автоматизации и генерирование огромного количества отдельных изме- нений. В этих условиях мы сочли полезным рассматривать определенные изменения как скот: безымянные и безликие фиксации, которые можно отменить или иным образом от- клонить в любой момент и с небольшими затратами, если это не затронет все стадо. Часто такое происходит из-за непредвиденной проблемы, не выявленной тестами, или даже из-за простого конфликта слияния. В отношении фиксации-любимца часто сложно не принять отказ на свой счет, но при работе с большим количеством изменений в рамках крупномасштабного изменения отказ — это просто особенности работы. Наличие автоматизации означает, что инструменты можно обновлять и вносить новые изменения с очень низкими затратами, поэтому периодическая потеря нескольких голов скота не воспринимается как проблема. Один из недостатков выполнения такого большого количества тестов состоит в том, что независимые и маловероятные события почти гарантированы в достаточно большом масштабе. Нестандартные и нестабильные тесты (глава 11), не наносящие вреда командам, которые их пишут и поддерживают, особенно трудны для авторов крупномасштабных изменений. Нестабильные тесты незначительно влияют на от- дельные команды, но могут серьезно повлиять на пропускную способность системы крупномасштабных изменений. Автоматические системы обнаружения и устране- ния нестабильных тестов помогают решить эту проблему, но могут потребоваться постоянные усилия, чтобы гарантировать, что команды, создающие нестабильные тесты, сами будут нести свои расходы. Получив опыт применения крупномасштабных изменений, сохраняющих семантику и генерируемых машиной, мы обрели уверенность в правильности отдельного из- менения. Тесты, проявившие нестабильность в недавнем прошлом, теперь игнори- руются нашими автоматизированными инструментами при отправке. Теоретически это означает, что отдельная часть изменения может вызвать регрессию, которая Процесс крупномасштабных изменений 469 обнаруживается только с помощью нестабильного теста. Но на практике это случа- ется настолько редко, что проще решить проблему в ходе человеческого общения, чем с помощью автоматизации. В процессе любого крупномасштабного изменения отдельные его части должны фиксироваться независимо. Это означает, что механизм деления может сгруппиро- вать зависимые изменения (например, файл заголовка и файл реализации) вместе. Как и любые другие изменения, части крупномасштабных изменений также должны проходить проверки в конкретных проектах перед обзором и фиксацией. Отправка изменений для обзора После проверки безопасности изменения Rosie отправляет это изменение подходя- щему рецензенту. В такой большой компании, как Google, насчитывающей тысячи инженеров, поиск рецензентов сам по себе является сложной задачей. Код в репо- зитории организован с помощью файлов OWNERS (главе 9) со списками пользова- телей, обладающих правом одобрения для определенного поддерева в репозитории. Rosie использует службу определения владельцев, которая читает файлы OWNERS и оценивает каждого владельца на предмет возможности его привлечения к обзору рассматриваемой части изменения. Если выбранный владелец не ответит в уста- новленный срок, Rosie автоматически добавит дополнительных рецензентов, чтобы обеспечить своевременный обзор изменения. В процессе рассылки изменений для обзора Rosie также запускает инструменты тестирования перед фиксацией для каждого проекта, которые могут выполнять дополнительные проверки. Для крупномасштабных изменений мы выборочно отключаем некоторые проверки, например проверки нестандартного форматиро- вания описания изменений. Такие проверки полезны для отдельных изменений в конкретных проектах, но они являются источником неоднородности в кодовой базе и могут значительно усложнить процесс крупномасштабных изменений. Эта неоднородность препятствует масштабированию наших процессов и систем, и нельзя требовать, чтобы инструменты и авторы крупномасштабных изменений понимали особые правила каждой команды. Мы также стараемся игнорировать ошибки, выявленные в ходе предварительной проверки и существовавшие до внесения рассматриваемого изменения. Работая над отдельным проектом, инженер может с легкостью исправить их и продолжить свою первоначальную работу, но этот метод не подходит для крупномасштабных изменений в кодовой базе Google. Владельцы проектов отвечают за отсутствие ра- нее существовавших ошибок в своей кодовой базе в рамках социального контракта между ними и инфраструктурными командами. Обзор Изменения, сгенерированные системой Rosie, должны пройти стандартный про- цесс обзора кода. Как показывает наш опыт, владельцы проектов не часто относятся к крупномасштабным изменениям так же строго, как к обычным изменениям, — они 470 Глава 22. Крупномасштабные изменения слишком доверяют инженерам, создающим крупномасштабные изменения. На прак- тике владельцы проектов только бегло просматривают эти изменения. Поэтому мы передаем владельцам только определенные изменения, чтобы они оценили их в контексте своих проектов. Все остальные изменения могут передаваться «глобаль- ному утверждающему лицу», имеющему право одобрять любые изменения во всем репозитории. Когда в процесс обзора вовлекается глобальный утверждающий, все отдельные части изменения передаются ему, а не владельцам разных проектов. Глобальные утверж- дающие обычно обладают экспертными знаниями языка и библиотек, которые они проверяют. Они взаимодействуют с авторами крупномасштабных изменений, стараясь узнать детали изменения и возможные отказы, и на основе полученных сведений выстраивают свой рабочий процесс. Вместо обзора каждого изменения по отдельности глобальные рецензенты исполь- зуют отдельный набор инструментов на основе паттернов для проверки каждого изменения и автоматического одобрения тех, что соответствуют их ожиданиям. Как результат, им приходится исследовать вручную только небольшую подгруппу изме- нений, в которой выявились аномалии из-за конфликтов слияния или неправильной работы инструментов, что обеспечивает хорошее масштабирование процесса. Отправка в репозиторий Наконец, отдельные изменения фиксируются в репозитории. Так же как на этапе обзора, перед фактической фиксацией в репозитории изменение проходит различ- ные проверки. Благодаря Rosie мы можем эффективно создавать, тестировать, рецензировать и фиксировать тысячи изменений в день во всей кодовой базе в Google и даем на- шим командам возможность эффективно переводить своих пользователей на новые системы. Технические решения, которые раньше имели бесповоротный характер, такие как выбор имени для широко используемого символа или местоположения популярного класса в кодовой базе, теперь стало возможным менять. Очистка Разные крупномасштабные изменения завершаются по-разному, от полного удаления старой системы до переноса только важных ссылок и оставления старых для исчез- новения естественным путем 1 . Почти во всех случаях важно иметь систему, которая предотвращает повторное появление символа или системы, которые удаляются крупномасштабным изменением. Мы в Google используем для этого платформу Tricorder (главы 19 и 20). Она отмечает новые попытки использовать устаревший объект и является эффективным средством для предотвращения отката назад. Под- робнее о процессе устаревания и прекращения поддержки в главе 15. 1 К сожалению, системы, которые хотелось бы убрать естественным путем, оказываются наиболее устойчивыми. Они — пластик в экосистеме кода. Итоги 471 Заключение Крупномасштабные изменения являются важной частью экосистемы разработки ПО в Google. Они открывают широкие возможности для проектирования, давая уверенность, что некоторые решения можно будет изменить потом. Процесс круп- номасштабных изменений также позволяет разработчикам базовой инфраструктуры удалять из кодовой базы в Google старые системы, версии языков и библиотечные идиомы, заменяя их новыми, и сохранять при этом целостность кодовой базы как в пространстве, так и во времени. И для всего этого достаточно нескольких десятков инженеров, поддерживающих десятки тысяч других сотрудников. Независимо от размера вашей организации подумайте, как вы будете вносить ради- кальные изменения в свою коллекцию исходного кода. Наличие такой возможности улучшит масштабирование вашей организации и поможет сохранить исходный код гибким долгое время. Итоги y Процесс крупномасштабных изменений позволяет иначе взглянуть на беспово- ротность некоторых технических решений. y Традиционные модели рефакторинга не подходят для масштабных изменений. y Реализация поддержки крупномасштабных изменений помогает выработать привычку проводить такие изменения. ГЛАВА 23 Непрерывная интеграция Автор: Рейчел Танненбаум Редактор: Лиза Кэри Непрерывная интеграция обычно определяется как «практика разработки ПО, когда участники команды часто интегрируют результаты своей работы <...> Каждая инте- грация проверяется автоматической сборкой (и тестированием) для максимально быстрого выявления ошибок» 1 . Проще говоря, главная цель непрерывной интегра- ции — как можно раньше и автоматически обнаружить проблемные изменения. Но что означает «частая интеграция» для современного распределенного при- ложения? Современные системы имеют множество движущихся частей, помимо кода последней версии в репозитории. Фактически, если следовать последней тенденции к использованию микросервисов, маловероятно, что изменения, на- рушающие работу приложения, будут находиться непосредственно в кодовой базе проекта, скорее они будут находиться в слабосвязанных микросервисах на другой стороне сетевого соединения. Традиционная непрерывная сборка тестирует изме- нения в двоичном файле, а ее расширенная версия может тестировать изменения в вышестоящих микросервисах. Зависимость просто переносится из стека вызовов функции в HTTP-запрос или RPC. Даже вдали от своих зависимостей приложение может периодически получать дан- ные или обновлять модели машинного обучения. Оно может работать в меняющихся операционных системах, средах выполнения, службах облачного хостинга и на устрой- ствах. Может быть функцией, находящейся на вершине растущей платформы, или платформой, вмещающей растущую базу функций. Все это следует рассматривать как зависимости, и мы должны стремиться «непрерывно интегрировать» изменения в них. Ситуация осложняется еще больше тем, что эти изменяющиеся компоненты часто принадлежат сторонним разработчикам, которые развертывают изменения по собственному графику. С учетом вышесказанного более точное определение непрерывной интеграции в со- временном мире, особенно при масштабной разработке, выглядит так: Непрерывная интеграция — это непрерывные сборка и тестирование всей сложной и быстро развивающейся экосистемы. 1 См. https://www.martinfowler.com/articles/continuousIntegration.html Глава 23. Непрерывная интеграция 473 Тестирование тесно связано с непрерывной интеграцией, и мы будем подчеркивать это на протяжении всей главы. В предыдущих главах мы обсудили широкий спектр видов тестирования, от юнит-тестирования до интеграционного и системного. С точки зрения тестирования непрерывная интеграция — это парадигма, помогаю- щая понять: y какие тесты и когда должны запускаться в рабочем процессе разработки при не- прерывной интеграции изменений в коде (и в других компонентах); y как сформировать SUT с учетом таких аспектов, как точность и затраты. Например, какие тесты запускать перед отправкой в репозиторий, какие — после от- правки, а какие еще позже, например на этапе развертывания в промежуточной среде? Соответственно как представить SUT в каждой из этих точек? Как вы уже наверня- ка догадались, требования к формированию SUT для выполнения проверок перед отправкой изменений в репозиторий могут значительно отличаться от требований к той же системе в промежуточной среде. Например, для приложения, собранного из кода, ожидающего рецензирования на этапе предварительной проверки, может быть опасно взаимодействовать с реальными производственными службами (подумайте о безопасности и лимите на уязвимости), тогда как часто это вполне приемлемо для тестирования в промежуточной среде. И почему мы должны пытаться оптимизировать этот нередко тонкий баланс «пра- вильных решений» в «правильное время» с помощью непрерывной интеграции? Богатый опыт прошлого показал выгоды непрерывной интеграции для инженерных организаций в частности и для бизнеса в целом 1 . Эти выгоды обусловлены мощной гарантией: наличием неоспоримых и своевременных доказательств, поддающихся проверке, готовности приложения к переходу на следующий этап. Нам не нужно просто надеяться, что все участники интеграции осторожны, ответственны и вни- мательны, — мы можем гарантировать работоспособность нашего приложения на разных этапах от сборки до выпуска, повышая тем самым качество наших продуктов и уверенность и продуктивность наших команд. В оставшейся части этой главы мы познакомимся с ключевыми идеями, передо- выми практиками и проблемами непрерывной интеграции, а затем рассмотрим, как она реализуется в Google, какие инструменты непрерывной сборки используются, немного поговорим о TAP и в заключение подробно изучим, как развивалась непре- рывная интеграция одного из приложений. 1 Форсгрен Н., Хамбл Дж., Ким Дж. Ускоряйся! Наука DevOps. Как создавать и масштаби- ровать высокопроизводительные цифровые организации. Интеллектуальная литература, 2020. — Примеч. пер. |