Делай как вGoogle
Скачать 5.77 Mb.
|
Что такое крупномасштабное изменение? Сначала определим, какие изменения считаются крупномасштабными. По наше- му опыту, крупномасштабное изменение — это любой набор изменений, которые логически связаны, но практически не могут быть представлены в виде атомарной Что такое крупномасштабное изменение? 453 единицы. Это может быть обусловлено вовлечением в изменение настолько большого количества файлов, что базовый инструмент не может зафиксировать их все сразу, или настолько большим объемом изменений, что попытка их слияния вызывает конфликты. Часто принадлежность изменений к категории крупномасштабных определяется топологией репозитория: если организация использует коллекцию рас- пределенных или объединенных репозиториев 1 , то внесение атомарных изменений в них может оказаться технически невозможным 2 . Подробнее о препятствиях для атомарных изменений далее в этой главе. В Google крупномасштабные изменения почти всегда создаются с использованием автоматизированных инструментов. Такие изменения делятся на несколько основ- ных категорий: y устранение типичных антипаттернов с помощью инструментов анализа всей кодовой базы; y замена вызовов устаревших библиотечных функций; y добавление улучшений низкоуровневой инфраструктуры, например обновление компилятора; y перевод пользователей со старой системы на новую 3 Количество инженеров, работающих над этими задачами, может быть небольшим, но их клиентам полезно иметь представление об инструментах и процессе крупномас- штабных изменений. По своей природе крупномасштабные изменения затрагивают большое количество клиентов, а инструменты, используемые для этого, легко мас- штабируются вниз и могут использоваться командами, вносящими всего несколько десятков связанных изменений. За конкретными крупномасштабными изменениями могут стоять более широкие изменения. Например, новый стандарт языка ввел более эффективную идиому для выполнения некоторой задачи, или интерфейс внутренней библиотеки изменился, или новая версия компилятора потребовала исправления существующих проблем, которые она интерпретирует как ошибки. В Google большинство крупномасштабных изменений на самом деле практически не влияют на функциональные возможности. Как правило, это обширные изменения в тексте, цель которых — увеличение ясности, оптимизация или улучшение совместимости в будущем. Но не все крупномасштабные изменения сохраняют поведение кодовой базы. В базах кода, по размерам сопоставимых с Google, командам разработчиков инфра- структуры может потребоваться регулярно менять сотни тысяч отдельных ссылок на 1 Некоторые идеи, помогающие понять причины, описаны в главе 16. 2 В таком объединенном мире можно сказать: «Мы просто будем выполнять фиксации в каждый репозиторий как можно быстрее, чтобы продолжительность перерывов в сборке была небольшой!» Но этот подход действительно не масштабируется с ростом числа объ- единенных репозиториев. 3 Подробнее об этой практике в главе 15. 454 Глава 22. Крупномасштабные изменения устаревший паттерн или символ. Пока в самых масштабных случаях мы затрагивали миллионы ссылок и надеемся, что процесс будет и дальше хорошо масштабироваться. Мы давно поняли, что выгодно как можно раньше вкладывать средства в инструмен- ты поддержки крупномасштабных изменений. Мы также поняли, что эффективные инструменты помогают инженерам вносить небольшие изменения. Инструменты, позволяющие эффективно изменять тысячи файлов, достаточно хорошо масштаби- руются вниз до уровня десятков файлов. Кто занимается крупномасштабными изменениями? В основном с крупномасштабными изменениями работают команды, занимающиеся инфраструктурой, которые создают наши системы и управляют ими. Но инструмен- ты и ресурсы доступны всем инженерам в компании. Если вы пропустили главу 1, то могли бы спросить, почему эту работу выполняют инфраструктурные команды. Почему нельзя просто добавить новый класс, функцию или систему и потребовать от всех, кто пользуется старой версией, перейти на обновленный аналог? С практи- ческой точки зрения это может выглядеть проще, но, как оказывается, этот подход плохо масштабируется по нескольким причинам. Во-первых, инфраструктурные команды, которые создают базовые системы и управ- ляют ими, обладают знаниями в предметной области, необходимыми для исправле- ния сотен тысяч ссылок. Команды, использующие инфраструктуру, вряд ли будут обладать достаточным уровнем компетенции для выполнения множества миграций, и нет смысла полагаться на повторное изучение опыта, который уже имеется у ин- фраструктурных команд. Централизация внесения изменений также позволяет бы- стрее восстанавливать работоспособность после ошибок, потому что ошибки обычно делятся на известные категории, и команда, выполняющая миграцию, может иметь сценарий — формальный или неформальный — их устранения. Представьте, сколько времени потребуется на выполнение первого из серии полу- механических изменений, которые вы не понимаете. Вероятно, вы потратите время на знакомство с обоснованием и характером изменения, найдете простой пример, попытаетесь понять, как в нем применяются предоставленные предложения, а за- тем применить их к своему коду. Повторение этого процесса в каждой команде в организации значительно увеличит общие затраты. Организовав небольшое число централизованных команд, ответственных за крупномасштабные изменения, мы в Google одновременно снизили эти затраты и обеспечили возможность проводить такие изменения более эффективно. Во-вторых, никому не нравится выполнять неоплачиваемую работу 1 . Новая система может быть многократно лучше той, которую она заменяет, но эти преимущества ча- 1 Под «неоплачиваемой работой» мы подразумеваем «выполнение дополнительных внешних требований без компенсации трудозатрат». К ней относится, например, требование носить вечерние платья по пятницам без прибавки к зарплате для покупки этих платьев. Кто занимается крупномасштабными изменениями? 455 сто распространяются по всей организации и потому вряд ли будут иметь достаточно большое значение для отдельных команд, которые пожелают выполнить обновление самостоятельно. Если новая система достаточно важна, затраты на миграцию будут нести разные подразделения. Централизованная миграция почти всегда проходит быстрее и обходится значительно дешевле, чем естественная миграция отдельных команд. Кроме того, наличие команд; владелец систем помогает согласовать стимулы для осуществления крупномасштабных изменений в этих системах. Как показывает наш опыт, естественные миграции редко оказываются полностью успешными, отчасти потому, что при разработке нового кода инженеры склонны опираться на примеры в существующем коде. Наличие команды, заинтересованной в удалении старой системы и ответственной за миграцию, помогает гарантировать успех миграции. Комплектование и финансирование команды для такого рода миграций может вы- глядеть как дополнительные расходы, но на самом деле этот шаг сужает влияние внешних эффектов, которые создает неоплачиваемая работа, и дает дополнительные преимущества масштабирования. КЕЙС: ЗАПОЛНЕНИЕ ДОРОЖНЫХ ВЫБОИН Системы поддержки крупномасштабных изменений используются в Google в основном для высокоприоритетных миграций, но мы также обнаружили, что их доступность открывает широкие возможности для различных мелких исправлений в нашей кодовой базе. Подобно дорожным организациям, которые строят новые дороги и ремонтируют старые, инфраструк- турные команды в Google занимаются не только разработкой новых систем и миграцией пользователей на них, но также много времени тратят на исправление существующего кода. Например, в начале нашей истории появилась библиотека шаблонов, дополняющая стандартную библиотеку шаблонов C++. Эта библиотека, которую мы назвали Google Template Library, состояла из нескольких заголовочных файлов, требующих реализации. По причинам, затерянным в тумане времени, один из этих файлов заголовков назывался stl_util.h , а другой — map-util.h (обратите внимание на разные разделители в именах файлов). Эта разница не только волновала сторонников последовательности, но и влекла снижение продуктивности: инженерам приходилось запоминать, в каком файле какой разделитель ис- пользовался, и обнаруживать ошибку после потенциально длительного цикла компиляции. Исправление единственного символа может показаться сложной задачей, особенно в такой кодовой базе, как в Google, но зрелость наших инструментов и процессов крупномасштаб- ных изменений позволили нам сделать это всего за пару недель в фоновом режиме. Авторы библиотеки могли находить и применять это изменение в массовом порядке, не беспокоя конечных пользователей. В результате мы сумели уменьшить количество сбоев во время сборки, вызванных этой проблемой. Повышение продуктивности (и удовлетворенности) с лихвой окупили время, потраченное на внесение изменений. Одновременно с совершенствованием возможности внесения изменений во всю нашу кодовую базу росло разнообразие изменений, и теперь мы можем принимать некоторые инженерные решения, будучи уверенными, что нам удастся изменить их в будущем. Ино- гда стоит приложить усилия, чтобы заполнить несколько выбоин. 456 Глава 22. Крупномасштабные изменения Препятствия к атомарным изменениям Прежде чем приступить к обсуждению фактического процесса крупномасштабных изменений, используемого в Google, мы должны поговорить о том, почему многие виды изменений нельзя зафиксировать атомарно. В идеальном мире все логические изменения можно упаковать в одну атомарную фиксацию, которую можно протести- ровать, оценить и зафиксировать независимо от других изменений. К сожалению, с ростом репозитория и числа работающих с ним инженеров этот идеал становится все менее достижимым даже в небольшом масштабе, в котором используется набор распределенных или объединенных репозиториев. Технические ограничения Начнем с того, что операции большинства VCS линейно масштабируются с размером изменения. Система может нормально обрабатывать небольшие фиксации (на- пример, затрагивающие около десятков файлов), но ей может не хватить памяти или вычислительной мощности для атомарной фиксации тысяч файлов. В центра- лизованных VCS фиксации могут блокировать другие операции, выполняющие запись (а в старых системах — и чтение), то есть большие фиксации задерживают других пользователей. Таким образом, иногда невозможно вносить большие изменения атомарно в конкрет- ной инфраструктуре. Деление большого изменения на более мелкие независимые фрагменты позволяет обойти эти ограничения, но усложняет процесс изменения 1 Конфликты слияния По мере увеличения размера изменения растет вероятность конфликтов слияния. Все известные нам VCS требуют обновления и слияния, возможно, проводимых вручную, если в центральном репозитории хранится более новая версия файла. По мере увеличения количества файлов в изменении (и инженеров, работающих с репозиторием) вероятность столкнуться с конфликтом слияния возрастает. В небольшой компании можно незаметно (в выходные) внести изменение, которое затронет каждый файл в репозитории. В некоторых компаниях даже может иметься неформальная система захвата глобальной блокировки репозитория путем передачи виртуального (или даже физического!) ключа команде разработчиков. Но в такой большой компании, как Google, подобные решения просто невозможны: кто-то всегда вносит изменения в репозиторий. При небольшом количестве изменяемых файлов вероятность конфликтов слияния уменьшается, поэтому эти файлы с большей вероятностью будут зафиксированы без проблем. Это свойство также справедливо для следующих областей. 1 См. https://ieeexplore.ieee.org/abstract/document/8443579 Препятствия к атомарным изменениям 457 Никаких кладбищ с привидениями В командах, отвечающих за надежность производственных служб Google, есть мантра: «Никаких кладбищ с привидениями». В данном случае под кладбищем с привидениями подразумевается система настолько старая и сложная, что никто не осмеливается в нее войти. Это может быть критически важная для бизнеса система, остановленная в развитии, потому что любая попытка ее изменения может привести к сбою, который будет стоить бизнесу реальных денег. Часто она представляет риск для существования бизнеса и может потреблять чрезмерный объем ресурсов. Однако кладбища с привидениями существуют не только в производственных системах — их можно найти в базах кода. У многих организаций есть устарев- шие и не поддерживаемые программные компоненты, написанные кем-то давно ушедшим из команды, и лежащие на пути к некоторым важным функциям, при- носящим доход. Эти системы также остановлены в развитии и похоронены под пластами бюрократических запретов, чтобы предотвратить изменения, которые могут вызвать нестабильность. Никто не хочет быть сапером, который перережет не тот проводок! Эти части кодовой базы исключены из процесса крупномасштабных изменений, потому что препятствуют массовым миграциям, выводу из эксплуатации других систем, на которые они полагаются, или обновлению используемых компиляторов и библиотек. Мы в Google обнаружили, что появлению таких кладбищ прекрасно противодей- ствует наше любимое тестирование. Имея всеобъемлющие и исчерпывающие тесты, мы можем вносить в ПО любые изменения, пребывая в уверенности, что ничего не нарушим независимо от старости или сложности системы. Написание тестов тре- бует больших усилий, но позволяет кодовой базе, такой масштабной, как в Google, развиваться в течение длительного времени, не превращаясь в дом с призраками. Неоднородность Крупномасштабные изменения действительно возможны, только когда большую часть работы по их применению выполняют компьютеры, а не люди. Насколько хорошо люди способны принимать неоднозначные решения, настолько же хорошо компьютеры справляются с правильным применением преобразований в нужных местах в согласованной среде. Если в вашей организации используется множество разных VCS, систем непрерывной интеграции и инструментов для конкретного про- екта или руководств по форматированию, то вам будет сложно внести радикальные изменения во всю кодовую базу. Упрощение окружения для большей согласованности поможет не только инженерам, но и роботам выполнять преобразования. Например, во многих проектах в Google настроено выполнение тестов перед от- правкой изменений в кодовую базу. Эти тесты могут быть очень сложными и про- верять самые разные аспекты — от присутствия новых зависимостей в белом списке 458 Глава 22. Крупномасштабные изменения до проверки связи изменения с ошибкой. Многие из этих проверок актуальны для команд, пишущих новые функции, но для крупномасштабных изменений они лишь добавляют ненужные сложности. Мы решили смириться со сложностями, такими как выполнение тестов перед отправкой, сделав сложные процедуры стандартными для всей кодовой базы, но рекомендуем командам опускать специальные проверки, когда крупномасштабные изменения затрагивают какие-то части их проектов. Большинство команд рады по- мочь в проведении крупномасштабных изменений, которые принесут пользу для их проектов. К автоматизированным инструментам также применимы многие преимущества согласованности для инженеров (глава 8). Тестирование Всякое изменение должно тестироваться (об этом мы поговорим чуть ниже), но чем крупнее изменение, тем сложнее протестировать его надлежащим образом. Систе- ма непрерывной интеграции в Google будет запускать не только тесты, напрямую связанные с измененными файлами, но и любые тесты, транзитивно зависящие от них 1 . То есть изменение будет охвачено тестами максимально широко, но чем дальше в графе зависимостей находится тест от файлов, затронутых изменением, тем ниже вероятность, что неудача при тестировании будет обусловлена самим изменением. Небольшие независимые изменения легче проверить, потому что каждое из них влияет на меньший набор тестов, а также потому, что обнаруженные в них ошибки легче диагностировать и исправлять. Найти основную причину неудачи тестирова- ния при изменении 25 файлов довольно просто, но поиск одного файла среди 10 000 сродни поиску иголки в стоге сена. Однако небольшие изменения ведут к тому, что одни и те же тесты будут выполнять- ся несколько раз, особенно тесты, которые зависят от больших разделов в кодовой базе. Поскольку время, которое инженеры тратят на поиск ошибок, выявленных при тестировании, намного дороже процессорного времени, необходимого для выпол- нения дополнительных тестов, мы сознательно пошли на этот компромисс. Однако он не всегда приемлем, поэтому вам стоит поискать правильный баланс для своей организации. 1 Это может показаться излишним, и, вероятно, так оно и есть. Мы ведем активные иссле- дования, стараясь определить наилучший способ выбора «правильного» набора тестов для конкретного изменения и найти баланс между временем выполнения тестов и трудозатратами в случае неправильного выбора. Препятствия к атомарным изменениям 459 КЕЙС: ТЕСТИРОВАНИЕ КРУПНОМАСШТАБНЫХ ИЗМЕНЕНИЙ Адам Бендер В настоящее время стало обычным делом, когда двузначный процент (от 10 до 20 %) изменений в проекте является результатом крупномасштабных изменений, то есть зна- чительный объем кода в проекте изменяется людьми, чья постоянная работа не связана с этим проектом. Без хороших тестов это было бы невозможно, и база кода в Google быстро атрофировалась бы под собственным весом. Крупномасштабные изменения позволяют нам систематически переносить всю кодовую базу на новые API, выводить из эксплуатации старые API, обновлять версии языков и избавляться от популярных, но опасных практик. Даже простое изменение сигнатуры функции выливается в сложный процесс, если затрагивает тысячи вызовов этой функции в сотнях разных продуктов и служб 1 . На- писав изменение, вы должны организовать обзор кода в десятках команд. Наконец, после одобрения вам потребуется провести как можно больше тестов, чтобы убедиться, что изменение безопасно 2 . Мы говорим «как можно больше», потому что крупномас- штабное изменение может привести к запуску каждого отдельного теста в Google, что может занять некоторое время. Фактически для многих крупномасштабных изменений приходится выделять время, чтобы выявить клиентов, чей код ломается в процессе крупномасштабного изменения. Тестирование крупномасштабного изменения — медленный и утомительный процесс. Когда изменение достаточно велико, локальная среда почти гарантированно рассинхронизиру- ется с главной ветвью, потому что окружающая база кода пересыпается как песок. В таких обстоятельствах вы заметите, что снова и снова запускаете тесты, только чтобы убедиться, что ваши изменения продолжают действовать. Когда в проекте присутствуют нестабильные тесты или код, не охваченный юнит-тестами, может потребоваться тестирование вручную, замедляющее весь процесс. Для ускорения мы используем стратегию под названием «поезд TAP» (test automation platform — автоматизированная платформа тестирования). |