Делай как вGoogle
Скачать 5.77 Mb.
|
446 Глава 21. Управление зависимостями y Зависимости часто выражаются как фиксированные. Разработчик libbase не может экспериментально проверить совместимость своих изменений с использованием тестов для liba и libb , если они явно зависят от конкретной фиксированной версии libbase y Мы могли бы явно включить историю и рейтинг в результаты непрерывной ин- теграции. Потеря работоспособности из-за предложенного изменения в проекте с длинной историей успешного тестирования выглядит намного более весомым аргументом для поиска ошибки, чем сбой в проекте, появившемся недавно и име- ющем историю сбоев тестирования по несвязанным причинам. В связи с этим возникает вопрос: с какими версиями каждой из зависимостей в сети вы тестируете изменения перед отправкой в репозиторий? Для тестирования всех комбинаций всех хронологических версий потребуется поистине ошеломляющий объем вычислительных ресурсов, даже по меркам Google. Наиболее очевидным упрощением политики выбора версии могло бы быть «тестирование текущей ста- бильной версии» (в конце концов, целью является разработка в главной ветви). Таким образом, модель управления зависимостями при неограниченных ресурсах фактически является моделью «жизни в главной ветви». Остается только опреде- лить, сможет ли эта модель эффективно применяться при использовании более реалистичного, ограниченного объема ресурсов и готовы ли разработчики API взять на себя больше ответственности за тестирование практической безопас- ности своих изменений. Выявление мест, где недостаточность ресурсов приводит к чрезмерному упрощению трудно вычислимой истины, продолжает оставаться полезным упражнением. Экспорт зависимостей До сих пор мы говорили только о входящих зависимостях, то есть о зависимостях от ПО, написанного другими людьми. Но стоит подумать также о том, как мы создаем ПО, которое можно использовать в качестве зависимости. Это выходит за рамки простой механики упаковки ПО и его выгрузки в репозиторий: мы должны подумать о выгодах, затратах и рисках предоставления ПО как для нас самих, так и для его потенциальных пользователей. Есть два основных пути, по которым безобидный и, надеюсь, благотворительный акт, такой как «открытие исходного кода библиотеки», может стать возможной потерей для организации. Во-первых, это может стать тормозом для репутации вашей орга- низации, если реализация не отличается высоким качеством или не поддерживается должным образом. Как говорится в сообществе Apache, на первое место мы должны ставить «сообщество, а не код». Даже если вы выпускаете отличный код, но избегаете участия в жизни сообщества, это все равно может нанести вред вашей организации и более широкому сообществу. Во-вторых, открытие исходного кода из благих по- буждений может ухудшить эффективность разработки, если вы не сможете удержать баланс. Со временем поддержка ответвлений станет слишком дорогостоящей. Управление зависимостями с бесконечными ресурсами 447 Пример: открытие исходного кода gflags В качестве примера ущерба репутации рассмотрим случай из истории Google, про- изошедший примерно в 2006 году, когда мы открыли исходный код наших библиотек управления флагами командной строки на C++. Конечно, вклад в сообщество от- крытого исходного кода — это исключительно хороший поступок, который ничем плохим нам не грозит, верно? К сожалению, нет. Множество причин как будто сго- ворились превратить этот хороший поступок во что-то, что точно навредило нашей репутации и, возможно, навредило сообществу открытого ПО: y В то время у нас не было возможности выполнять крупномасштабный рефакто- ринг, поэтому весь код, использовавший эту библиотеку, должен был оставаться неизменным — мы не могли переместить код в новое место в кодовой базе. y Мы разделили наш репозиторий на «код, разработанный внутри организации» (который можно свободно копировать для создания производных при правиль- ном подходе к переименованию) и «код, использование которого может иметь юридические/лицензионные последствия» (имеющий некоторые ограничения на использование). y Если проект открытого ПО принимает код от внешних разработчиков, это, как правило, влечет юридические проблемы: автор проекта не владеет этим кодом, он лишь имеет право использовать его. В результате проект gflags был обречен превратиться либо «в отрезанный ломоть», либо в отдельную производную. Исправления, предлагаемые проекту, невозможно было включить в исходный код внутри Google, мы не могли переместить проект в пределах нашего монолитного репозитория, потому что еще не освоили эту фор- му рефакторинга, и мы не могли использовать открытую версию как зависимость в своем внутреннем коде. Кроме того, как и в большинстве организаций, наши приоритеты менялись со вре- менем. Примерно в то же время мы заинтересовались продуктами, выходящими за рамки нашего традиционного пространства (веб-приложения, поиск), такими как Google Earth, который имел более традиционный механизм распространения в виде предварительно скомпилированных двоичных файлов для различных платформ. В конце 2000-х было необычным, хотя и не единичным случаем, когда библиотека из нашего монолитного репозитория, особенно такая низкоуровневая, как gflags, ис- пользовалась на различных платформах. С течением времени и ростом Google наше внимание сузилось до такой степени, что стало крайне редким явлением создание какой-либо библиотеки с использованием чего-либо, кроме нашей собственной на- строенной инструментальной цепочки, с последующим развертыванием в нашем производственном парке. Решение проблем «переносимости» для правильной под- держки открытого проекта, такого как gflags, стало почти невозможным: наши вну- тренние инструменты просто не поддерживали эти платформы, и нашему среднему разработчику не приходилось взаимодействовать с внешними инструментами. Это была постоянная борьба за сохранение переносимости. 448 Глава 21. Управление зависимостями Первоначальные авторы и сторонники открытого ПО постепенно переходили в дру- гие компании или в другие команды, и настал момент, когда никто изнутри больше не поддерживал открытый проект gflags. Учитывая, что проект не являлся основной работой конкретной команды и никто не мог сказать, почему важно продолжать развивать его, мы фактически позволили ему начать гнить снаружи 1 . Внутренняя и внешняя версии со временем расходились все дальше друг от друга, и, в конце концов, некоторые внешние разработчики создали свой проект на основе внешней версии и уделили ему должное внимание. Кроме: «О, смотрите, Google внесла свой вклад в мир открытого ПО», ничто из пере- численного не укрепляло нашу репутацию, и, тем не менее, все это имело смысл, если учесть приоритеты нашей организации. Те, кто был близок к этому, надежно усвоили урок: «Не выпускайте ничего, если не планируете поддерживать это в долгосрочной перспективе». Усвоили ли этот урок все инженеры Google — еще предстоит выяснить. Мы слишком большая организация. Помимо туманного «мы плохо выглядим» эта история также показывает, как легко столкнуться с техническими проблемами из-за некачественно подготовленных и плохо поддерживаемых внешних зависимостей. Библиотека gflags была общедо- ступной, хотя и игнорировалась, но в Google все еще оставались проекты с откры- тым исходным кодом или проекты, которые должны были оставаться доступными за пределами нашей экосистемы монолитного репозитория. Неудивительно, что авторы этих других проектов смогли выявить 2 общее подмножество API для вну- тренней и внешней версий библиотеки. Поскольку это общее подмножество почти не менялось в течение длительного времени, оно постепенно превратилось в «луч- ший фундамент» для тех редких команд, которые имели необычные требования к переносимости примерно в период с 2008 по 2017 год. Их код мог встраиваться как во внутреннюю, так и во внешнюю экосистему, отключая производные версии библиотеки gflags, в зависимости от среды. Затем по не связанным с этим причинам команды разработчиков библиотек на C++ начали изменять наблюдаемые, но не документированные части внутренней реа- лизации gflags. В этот момент все, кто зависел от стабильности и эквивалентности неподдерживаемой внешней версии, начали кричать, что их сборки и версии вне- запно сломались. Возможность оптимизации ценой нескольких тысяч совокупных процессоров во всем парке Google значительно задержалась, но не потому, что было сложно обновить API, от которого зависело 250 миллионов строк кода, а потому, что небольшая горстка проектов полагалась на непредсказуемое и неожиданное поведение. И снова проявилось действие закона Хайрама, в данном случае даже в отношении разветвленных API, поддерживаемых разными организациями. 1 Нельзя сказать, что это правильно или мудро, просто как организация мы позволяем чему- то ускользать от нашего внимания. 2 Часто методом проб и ошибок. Управление зависимостями с бесконечными ресурсами 449 КЕЙС: APPENGINE Еще более наглядный пример, как мы подвергаемся риску неожиданной технической зависимости, — публикация службы Google AppEngine. Эта служба дает пользователям возможность писать свои приложения поверх существующей инфраструктуры на одном из нескольких популярных языков программирования. Если приложение придерживается правильной модели управления хранилищем и состоянием, служба AppEngine способна масштабировать его до огромной величины: внутреннее хранилище и внешний интерфейс управляются и клонируются по мере необходимости производственной инфраструктурой Google. Первоначально поддержка Python в AppEngine представляла собой 32-битную сборку, работающую со старой версией интерпретатора. Сама система AppEngine была (конечно) реализована в нашем монолитном репозитории и собиралась вместе с нашими общими инструментами на Python и C++. В 2014 году мы начали процесс существенного обнов- ления среды выполнения Python вместе с нашим компилятором C++ и стандартной би- блиотекой, в результате чего мы эффективно связали «код, который создается с помощью текущего компилятора C++», с «кодом, использующим обновленную версию Python». Этот проект обновил одновременно две зависимости. Для большинства проектов это не было проблемой. А для нескольких проектов, из-за пограничных ситуаций и действия за- кона Хайрама, наши эксперты по языковым платформам провели исследование и отладку, чтобы облегчить переход. Однако разработчики AppEngine обнаружили ужасающее про- явление закона Хайрама в практической плоскости бизнеса. Многие пользователи — наши клиенты, которые платят деньги, — не смогли (или не захотели) обновиться. Кто-то не хотел переходить на более новую версию Python, кто-то не мог позволить себе изменить потребление ресурсов, связанное с переходом с 32-битной версии Python на 64-битную. Поскольку некоторые наши клиенты платили весьма значительные суммы за услуги AppEngine, команда AppEngine смогла убедительно обосновать необходимость отсрочки принудительного перехода на новые версии языка и компилятора. По сути это означало, что каждый фрагмент кода на C++ в транзитивном замыкании зависимостей от AppEngine должен быть совместим со старыми версиями компилятора и стандартной библиотеки: любые исправления ошибок или оптимизации производительности, которые могли быть внесены в эту инфраструктуру, должны были сохранять совместимость между версиями. Такая ситуация сохранялась почти три года. При наличии достаточного количества пользователей кто-то обязательно станет полагаться на некое «наблюдаемое» проявление вашей системы. Мы в Google ограничиваем своих внутренних пользователей рамками нашего технологического стека и обеспечиваем прозрачность с помощью монолитного репозитория и систем индексации кода, поэтому нам гораздо проще гарантировать возможность добавления полезных изменений. Когда мы переходим от управления исходным кодом к управ- лению зависимостями и теряем представление о том, как используется код, или подчиняемся конкурирующим приоритетам внешних групп (особенно тех, которые нам платят), становится гораздо труднее идти на чисто инженерные компромиссы. Выпуск API любого рода порождает возможность появления конкурирующих при- оритетов и непредвиденных ограничений с внешней стороны. Это не означает, что вам не следует выпускать API, а лишь предупреждает, что обслуживание внешних пользователей API обходится намного дороже, чем внутренних. 450 Глава 21. Управление зависимостями Совместное использование кода с внешним миром, будь то версия с открытым или с закрытым исходным кодом, — это не просто вопрос благотворительности (в случае открытого ПО) или бизнес-возможностей (в случае закрытого ПО). Зависящие пользователи в разных организациях с разными приоритетами, которых вы не мо- жете контролировать, в итоге начнут действовать по закону Хайрама в отношении этого кода. Если вы работаете с большими временными рамками, невозможно точно предсказать набор необходимых или полезных изменений, которые могут стать цен- ными. Принимая решение о выпуске чего бы то ни было, помните о долгосрочных рисках: изменение внешних общих зависимостей часто обходится намного дороже с течением времени. Заключение Управление зависимостями — сложная задача, и мы продолжаем искать решения для управления сложными поверхностями API и сетями зависимостей, где разработчи- ки зависимостей обычно не предполагают координации. Фактическим стандартом управления сетью зависимостей является SemVer, предоставляющее сжатую (с по- терями) информацию о возможном риске принятия какого-либо изменения. SemVer предполагает возможность предсказать серьезность изменения без учета информации о том, как используется рассматриваемый API. Однако SemVer достаточно хорошо работает в малых масштабах и еще лучше при использовании подхода MVS. По мере расширения сети зависимостей проблемы с законом Хайрама и потеря точности в SemVer делают управление выбором новых версий все более сложным. Однако мы постепенно движемся к миру, в котором оценки совместимости, предо- ставляемые разработчиками (семантические номера версий), будут заменены на до- казательства, основанные на опыте, — результаты тестирования зависимых пакетов. Если разработчики API возьмут на себя больше ответственности за тестирование своих изменений совместно с пользователями и четко объявят, какие типы измене- ний ожидаются, у нас появится возможность создавать сети зависимостей с более высокой точностью в еще большем масштабе. Итоги y Старайтесь заменить задачи управления зависимостями задачами управления версиями: если есть возможность создать больше кода внутри своей организации, то это существенно упростит прозрачность и координацию зависимостей. y Добавление зависимости в проект разработки ПО обходится дорого, и установле- ние постоянных доверительных отношений является сложной задачей. Импорт зависимостей в вашу организацию должен выполняться осторожно, с учетом текущих затрат на поддержку. y Зависимость — это контракт, компромисс, согласно которому производители и потребители имеют определенные права и обязанности. Производители должны Итоги 451 четко понимать, что они дают обещание, которое придется выполнять с течением времени. y SemVer — это сжатая (с потерями) оценка, выражающая, «насколько рискованно изменение». SemVer с SAT-решателем в диспетчере пакетов интерпретирует эту оценку как абсолютную. Это может приводить к ограничению, чрезмерному (ад зависимостей) или недостаточному (когда версии, которые должны работать вместе, оказываются несовместимыми). y Тестирование и непрерывная интеграция предоставляют фактическое свидетель- ство совместимости и работоспособности нового набора зависимостей. y Политики обновления до минимальной версии в SemVer и управления пакетами наиболее точны. Они все еще зависят от способности людей точно оценивать риск увеличения версии, но значительно повышают вероятность, что эксперт верно оценит связи между производителем и потребителем API. y Юнит-тестирование, непрерывная интеграция и (недорогие) вычислительные ре- сурсы могут изменить подход к управлению зависимостями. Для такого фазового перехода требуется фундаментально изменить взгляд на проблему управления зависимостями в отрасли, а также повысить ответственность производителей и потребителей. y Предоставление зависимости не проходит бесследно: простой подход «передать и забыть» может стоить вам репутации и стать проблемой для совместимости. Необходимость поддерживать стабильность может ограничить вам широту вы- бора дальнейших действий и сузить круг внутреннего использования. Поддержка без стабильности может подорвать ваш престиж или подвергнуть вас риску со стороны важных потребителей, зависящих от чего-то в соответствии с законом Хайрама и нарушающих ваши «нестабильные» планы. ГЛАВА 22 Крупномасштабные изменения Автор: Хайрам Райт Редактор: Лиза Кэри Подумайте немного о своей кодовой базе. Сколько файлов вы сможете надежно обно- вить за одну фиксацию? Какие факторы ограничивают это число? Вы когда-нибудь пробовали выполнить настолько крупное изменение? Сможете ли вы провести его в разумные сроки в чрезвычайной ситуации? Как соотносится размер самой большой выполненной вами фиксации с фактическим размером вашей кодовой базы? Как бы вы протестировали такое изменение? Скольким людям потребуется оценить это изменение, прежде чем оно будет принято? Сможете ли вы отменить это изменение после фиксации? Ответы на эти вопросы могут вас удивить (и то, что вы думаете о них, и то, что они на самом деле значат для вашей организации). Мы в Google давно отказались от идеи внесения в кодовую базу атомарных изме- нений большого объема. Как показывает наш опыт, с ростом кодовой базы и числа работающих с ней инженеров максимально возможное атомарное изменение, как ни странно, уменьшается, потому что затрудняется выполнение предварительных проверок и тестов для него и теряется возможность гарантировать перед его отправ- кой актуальность каждого его файла. Поскольку вносить радикальные изменения в нашу кодовую базу становится все труднее, но мы стремимся постоянно улучшать базовую инфраструктуру, нам пришлось разработать новые способы обоснования крупномасштабных изменений и способов их реализации. В этой главе мы поговорим о методах, как социальных, так и технических, которые позволяют поддерживать гибкость большой кодовой базы в Google и реагировать на изменения в базовой инфраструктуре. Мы рассмотрим несколько реальных приме- ров использования этих методов. Ваша база кода может отличаться от кодовой базы в Google, но понимание описываемых здесь принципов и их адаптация помогут вашей софтверной организации расширятся и иметь возможность вносить существенные изменения в кодовую базу. |