Делай как вGoogle
Скачать 5.77 Mb.
|
Поездка на поезде TAP Крупномасштабные изменения редко связаны друг с другом, и большинство затронутых тестов успешно выполнятся для большинства крупномасштабных изменений. Поэтому мы можем тестировать сразу несколько изменений и уменьшить общее количество вы- полняемых тестов. Модель поезда оказалась очень эффективной для тестирования круп- номасштабных изменений. Поезд TAP использует в своих интересах два обстоятельства: • Крупномасштабные изменения обычно являются результатом рефакторинга и поэтому имеют очень узкий охват, сохраняя локальную семантику. • Отдельные изменения часто проверяются проще и тщательнее, поэтому чаще всего они правильные. 1 Самая длинная серия из когда-либо выполнявшихся крупномасштабных изменений в те- чение трех дней удалила из репозитория больше миллиарда строк кода. Главной целью этого изменения было удаление устаревшего кода из репозитория, который переселился в новый дом. Только подумайте: насколько вы должны быть уверены в себе, чтобы удалить миллиард строк кода? 2 Крупномасштабные изменения обычно поддерживаются инструментами, позволяющими относительно легко находить, вносить и анализировать изменения. 460 Глава 22. Крупномасштабные изменения Модель поезда также имеет еще одно преимущество: она работает сразу с несколькими из- менениями и не требует, чтобы каждое отдельное изменение выполнялось изолированно 1 Поезд выполняет пять остановок и отправляется в путь каждые три часа: 1. Для каждого изменения в поезде запускается выборка из 1000 случайных тестов. 2. Отбираются все изменения, успешно прошедшие 1000 тестов, и из них создается «поезд». 3. Для изменений в поезде выполняются все тесты, непосредственно затронутые этими изменениями. При достаточно объемном (или низкоуровневом) крупномасштабном изменении это может означать выполнение всех тестов, имеющихся в репозитории Google. Этот процесс может занять больше шести часов. 4. Каждый тест, потерпевший неудачу, повторно запускается для каждого отдельного из- менения в поезде, чтобы определить, какие из них вызвали сбой. 5. Платформа TAP генерирует отчет для каждого изменения в поезде. В отчете описаны все выполненные и невыполненные цели, и он может использоваться как доказательство, что крупномасштабное изменение можно без опаски отправить в репозиторий. Код-ревью Наконец, все изменения должны проходить процесс обзора (глава 9), и это правило действует также в отношении крупномасштабных изменений. Обзор масштабных фиксаций может быть утомительным, обременительным и даже приводить к ошиб- кам, особенно если изменения вносятся вручную (процесс, которого следует избегать, о чем мы поговорим далее в этой главе). Чуть ниже мы расскажем, как в обзоре могут помочь автоматизированные инструменты, но для некоторых классов изменений мы предпочитаем, чтобы их правильность проверялась людьми. Разбивка крупномас- штабных изменений на части значительно упрощает задачу обзора. КЕЙС: ПЕРЕИМЕНОВАНИЕ SCOPED_PTR В STD::UNIQUE_PTR С самого начала в кодовой базе на C++ в Google существовал интеллектуальный указатель для упаковки объектов C++, размещаемых в куче, который обеспечивал их автоматическое уничтожение и освобождение памяти, а затем покидал область видимости. Этот указатель назывался scoped_ptr и широко использовался в кодовой базе в Google для гарантии над- лежащего управления временем жизни объектов. Он не был идеальным, но с учетом ограни- чений действующего на тот момент стандарта C++98 этот тип делал программы безопаснее. В C++11 появился новый стандартный тип: std::unique_ptr. Он выполнял ту же функ- цию, что и scoped_ptr, но дополнительно предотвращал ряд других классов ошибок. Тип std::unique_ptr был намного лучше, чем scoped_ptr, но в кодовой базе Google остава- лось еще более 500 000 ссылок на scoped_ptr, разбросанных среди миллионов файлов с исходным кодом. Для перехода на более современный тип потребовалось самое большое на тот момент крупномасштабное изменение. 1 Можно запросить у платформы автоматизированного тестирования выполнить «изо- лированный» прогон для единственного изменения, но это очень дорого и такие запросы выполняются только в часы затишья. Инфраструктура для крупномасштабных изменений 461 В течение нескольких месяцев несколько инженеров параллельно занимались этим во- просом. Используя крупномасштабную инфраструктуру миграции Google, они смогли заменить ссылки scoped_ptr на std::unique_ptr, а также постепенно придать типу scoped_ptr поведение, более близкое к поведению std::unique_ptr. На пике процесса миграции они ежедневно генерировали, тестировали и фиксировали более 700 независи- мых изменений, затрагивавших до 15 000 файлов. В настоящее время, усовершенствовав методы и улучшив инструменты, мы иногда достигаем пропускную способность, в десять раз превышающую тот результат. Как и многие другие, это крупномасштабное изменение имело длинный хвост нюансов по- ведения (еще одно проявление закона Хайрама), состояний гонки с другими инженерами и содержало использования в сгенерированном коде, которые не обнаруживались нашими автоматизированными инструментами, но выявлялись инфраструктурой тестирования, и мы продолжали работать над ними вручную. scoped_ptr также использовался как параметр типа в некоторых широко используемых API, что затрудняло выполнение небольших и независимых изменений. Мы хотели напи- сать систему анализа графа вызовов, которая могла бы изменить API и вызывающие его стороны транзитивно, за одну фиксацию, но были обеспокоены тем, что получающиеся изменения сами по себе будут слишком большими для атомарной фиксации. В конце концов, мы смогли удалить все ссылки на scoped_ptr, сначала сделав его псевдо- нимом типа std::unique_ptr, а затем выполнив текстовую замену старого псевдонима на новый и, наконец, полностью удалить старый псевдоним scoped_ptr. В настоящее время база кода в Google пользуется всеми преимуществами от использования того же стандарт- ного указателя, что и остальная экосистема C++, и это стало возможным только благодаря нашим технологиям и инструментам для крупномасштабных изменений. Инфраструктура для крупномасштабных изменений Компания Google вложила значительные средства в инфраструктуру поддержки крупномасштабных изменений, которая включает инструменты для их создания, кон- троля, анализа и тестирования. Однако наиболее важной поддержкой стало развитие культурных норм вокруг крупномасштабных изменений и их соблюдение. Наборы технических и социальных инструментов в разных организациях могут отличаться, но общие принципы инфраструктуры должны быть одинаковыми. Политики и культура Большая часть исходного кода в Google хранится в едином монолитном репозито- рии (глава 16), и каждый инженер может видеть почти весь этот код. Такая степень открытости означает, что любой инженер может отредактировать любой файл и от- править свои изменения для обзора тем, кто может их одобрить. Однако каждая из таких правок требует затрат как на создание, так и на проверку 1 1 Здесь становятся очевидными технические затраты с точки зрения вычислений и хранения, но трудозатраты на своевременный анализ изменения намного перевешивают технические. 462 Глава 22. Крупномасштабные изменения Исторически эти затраты были в некоторой степени симметричными, что ограничивало объем изменений, которые могли произвести один инженер или команда. По мере со- вершенствования инструментария поддержки крупномасштабных изменений в Google стало проще создавать большое количество изменений с очень небольшими затратами, и один инженер мог с легкостью загрузить работой большое количество рецензентов. Мы, конечно, поощряем повсеместное улучшение кодовой базы, но хотим быть уве- ренными, что они хорошо продуманы и не выполняются хаотично и бесконтрольно 1 И мы упростили процесс одобрения для команд и отдельных лиц, стремящихся выполнять крупномасштабные изменения. За этим процессом наблюдает группа опытных инженеров, знакомых с нюансами разных языков, а для оценки конкрет- ных изменений приглашаются эксперты в предметной области. Цель процесса не в запрете крупномасштабных изменений, а в желании помочь их авторам создать самые лучшие изменения, максимально использующие технический и кадровый капитал Google. Иногда эта группа может решить, что предложенная чистка кода, например устранение типичной опечатки без какого-либо способа предотвратить ее повторение, не стоит внимания и затрат. С этой политикой связано изменение культурных норм, касающихся крупномасштаб- ных изменений. Для владельцев кода важно чувствовать ответственность за свое ПО, но им также важно понимать, что крупномасштабные изменения являются важной со- ставляющей усилий Google по расширению наших практик разработки ПО. Так же как команды программных продуктов лучше всего знакомы со своим ПО, команды библио- течной инфраструктуры хорошо знают нюансы своей инфраструктуры, и заставить команды программных продуктов доверять опыту экспертов в предметной области яв- ляется важным шагом на пути к всеобщему признанию крупномасштабных изменений. В результате этого культурного сдвига команды программных продуктов набрались опы- та и стали доверять авторам крупномасштабных изменений в их предметной области. Иногда владельцы кода ставят под сомнение цель конкретной фиксации, сделанной в рамках более широкого крупномасштабного изменения, и авторы изменений от- вечают на их комментарии так же, как и на комментарии рецензентов. В социальном плане важно, чтобы владельцы кода понимали цель изменений в их ПО, но также осознавали, что не имеют права вето в отношении более широкого крупномас- штабного изменения. Со временем мы обнаружили, что подробные ответы на часто задаваемые вопросы и солидный послужной список улучшений вызывают широкую поддержку крупномасштабных изменений в Google. Понимание кодовой базы Для проведения крупномасштабных изменений бесценной оказалась возможность крупномасштабного анализа нашей кодовой базы как на уровне текста, с использо- ванием традиционных инструментов, так и на семантическом уровне. Например, 1 Например, мы не хотим, чтобы полученные инструменты использовались в качестве меха- низма для борьбы за правильную орфографию в комментариях. Инфраструктура для крупномасштабных изменений 463 инструмент семантического индексирования Kythe ( https://kythe.io ) позволяет полу- чить полную карту связей между сегментами нашей кодовой базы, что дает возмож- ность отвечать на вопросы: «Откуда вызывается эта функция?» или «Какие классы наследуют этот класс?» Kythe и другие подобные инструменты также предоставляют программный доступ к своим данным, позволяя включить их в инструменты рефак- торинга. (Дополнительные примеры в главах 17 и 20.) Мы также используем индексы, создаваемые компилятором, для анализа и преобра- зования нашей кодовой базы на основе абстрактного синтаксического дерева. Такие инструменты, как ClangMR ( https://oreil.ly/c6xvO ), JavacFlume и Refaster ( https://oreil. ly/Er03J ), которые могут выполнять преобразования параллельно, используют эти данные. Авторы небольших изменений могут использовать специализированные на- страиваемые инструменты, perl или sed, сопоставление с регулярными выражениями или даже простые сценарии командной оболочки. Какой бы инструмент ни использовала организация для создания изменений, важно, чтобы человеческие трудозатраты линейно масштабировались вместе с базой кода. Время на создание коллекции всех необходимых изменений не должно зависеть от размера репозитория. Инструменты для создания изменений тоже должны иметь возможность охватывать всю кодовую базу, чтобы автор мог убедиться, что его из- менение охватывает все случаи, которые он пытается исправить. Как и в других областях, затрагиваемых в этой книге, вложение средств, сил и вре- мени в инструменты обычно с лихвой окупается в краткосрочной или среднесроч- ной перспективе. Как показывает наш опыт, если для изменения требуется более 500 правок, то будет намного эффективнее, если инженер научится пользоваться инструментами для создания изменений, вместо внесения этих правок вручную. Опытные «чистильщики кода» делают намного меньше правок. Управление кодом Наиболее важной частью инфраструктуры крупномасштабных изменений являет- ся набор инструментов, которые делят основное изменение на более мелкие части и управляют процессом тестирования, отправки запросов по почте, обзором и фик- сацией изменений. В Google этот инструмент называется Rosie, и мы обсудим его более подробно чуть ниже, когда поближе познакомимся с нашим процессом круп- номасштабных изменений. Во многих отношениях Rosie — это не просто инструмент, а целая платформа для создания крупномасштабных изменений в масштабе Google. Он дает возможность разбивать большие наборы обширных изменений, произво- димых с помощью инструментов, на более мелкие, которые можно тестировать, рецензировать и отправлять в репозиторий независимо друг от друга. Тестирование Тестирование — еще одна часть инфраструктуры, обеспечивающей крупномас- штабные изменения. Тесты — это один из важнейших способов проверки работо- 464 Глава 22. Крупномасштабные изменения способности ПО (глава 11). Они особенно необходимы при применении изменений, созданных автоматически. Надежная культура и инфраструктура тестирования дают уверенность другим инструментам, что эти изменения не приведут к непред- виденным последствиям. Стратегия тестирования крупномасштабных изменений в Google немного отличается от политики тестирования обычных изменений, но обе политики используют одну базовую инфраструктуру непрерывной интеграции. Тестирование крупномасштаб- ных изменений гарантирует не только то, что большое изменение не вызовет сбоев, но и что каждый его сегмент можно безопасно и независимо отправить в репозито- рий. Поскольку каждый сегмент может содержать произвольные файлы, мы не ис- пользуем стандартные предварительные тесты, как для обычных проектов. Вместо этого прогоняем для каждого сегмента все тесты, которые он может затронуть, как обсуждалось выше. Поддержка языка Крупномасштабные изменения в Google обычно выполняются отдельно для каждо- го языка, и некоторые языки позволяют производить такие изменения проще, чем другие. Мы обнаружили, что некоторые особенности языка, такие как поддержка псевдонимов типов и возможность передачи функций, неоценимы, поскольку по- зволяют пользователям продолжать работу, пока мы вводим новые системы и пере- водим на них пользователей. Для языков, в которых отсутствуют эти возможности, часто бывает сложно осуществлять пошаговый переход 1 Мы также обнаружили, что в языках со статической типизацией намного проще выполнять большие автоматические изменения, чем в языках с динамической типизацией. Инструменты на основе компилятора вместе со строгим статическим анализом дают значительный объем информации, которую можно использовать в инструментах для анализа крупномасштабных изменений и отклонения недо- пустимых преобразований еще до того, как они дойдут до этапа тестирования. До- садным следствием этой особенности является большая сложность сопровождения языков с динамической типизацией, таких как Python, Ruby и JavaScript. Выбор языка часто тесно связан с продолжительностью жизни кода: языки, которые, как правило, более ориентированы на продуктивность разработчиков, нередко труднее поддерживать. Наконец, стоит отметить, что средства автоматического форматирования кода тоже являются важной частью инфраструктуры крупномасштабных изменений. Мы много внимания уделяем удобочитаемости исходного кода и хотим быть уверены, что любые изменения, производимые автоматизированными инструментами, будут понятны не только рецензентам, но и будущим читателям кода. Все инструменты, генериру- ющие крупномасштабные изменения, запускают автоматическое форматирование, 1 Язык Go недавно представил подобные возможности специально для поддержки крупно- масштабных изменений (см. https://talks.golang.org/2016/refactor.article ). Процесс крупномасштабных изменений 465 соответствующее языку, отдельно, поэтому инструментам, главной целью которых является собственно изменение, не нужно заботиться о правилах форматирования. Применение автоматического форматирования, например google-java-format ( https:// github.com/google/google-java-format ) или clang-format ( https://clang.llvm.org/docs/ ClangFormat.html ), к нашей кодовой базе обеспечивает единообразное оформление, которое упрощает разработку в будущем. Без автоматического форматирования крупномасштабные автоматизированные изменения ни за что не заслужили бы такое высокое доверие в Google. КЕЙС: ОПЕРАЦИЯ ROSEHUB Крупномасштабные изменения стали важной частью внутренней культуры Google, но они начинают обретать популярность и за его пределами. Самым известным случаем стала «Операция RoseHub» ( https://oreil.ly/txtDj ). В начале 2017 года в библиотеке Apache Commons была обнаружена уязвимость, позво- лявшая скомпрометировать любое Java-приложение, использующее уязвимую версию библиотеки прямо или опосредованно. Эта уязвимость стала известна как «Mad Gadget» (безумный гаджет). Среди прочего она позволила алчному хакеру зашифровать системы Управления общественным городским транспортом Сан-Франциско и остановить его де- ятельность. Поскольку для уязвимости достаточно, чтобы библиотека находилась где-то на пути к классам, то все, что зависело хотя бы от одного из многих проектов с открытым исходным кодом на GitHub, оказалось уязвимо. Чтобы решить эту проблему, некоторые инициативные гуглеры запустили свою версию про- цесса крупномасштабного изменения. Используя такие инструменты, как BigQuery ( https:// cloud.google.com/bigquery ), они определили проекты, подверженные уязвимости, и отпра- вили более 2600 исправлений для обновления их версий библиотеки Commons до версии, исправляющей уязвимость Mad Gadget. Вместо автоматизированных инструментов про- цессом управляли более 50 человек. Процесс крупномасштабных изменений Теперь мы поговорим о процессе создания крупномасштабных изменений. Он делится примерно на четыре этапа (с очень размытыми границами между ними): 1. Авторизация. 2. Создание изменения. 3. Управление частями изменения. 4. Очистка. Обычно эти этапы выполняются после того, как были написаны новые система, класс или функция, но о них важно помнить еще на этапе проектирования. Мы в Google стремимся разрабатывать системы-преемники с учетом путей миграции со старых систем, чтобы специалисты, сопровождающие систему, могли автоматически пере- водить своих пользователей на новую систему. |