Главная страница
Навигация по странице:

  • Ускорение выполнения тестов

  • Устранение нестабильности

  • Увеличение понятности тестов

  • Владение большими тестами

  • Почему необходимо заботиться об устаревании

  • Почему устаревание вызывает такие сложности

  • Планирование прекращения поддержки системы на этапе ее проектирования

  • Подходы к прекращению поддержки

  • Предупреждение о прекращении поддержки

  • Управление процессом прекращения поддержки

  • Инструменты для миграции

  • Делай как вGoogle


    Скачать 5.77 Mb.
    НазваниеДелай как вGoogle
    Дата31.05.2022
    Размер5.77 Mb.
    Формат файлаpdf
    Имя файлаDelay_kak_v_Google_Razrabotka_programmnogo_obespechenia_2021_Tom.pdf
    ТипДокументы
    #559735
    страница39 из 69
    1   ...   35   36   37   38   39   40   41   42   ...   69
    Выполнение больших тестов
    Поскольку наши большие тесты не вписываются в структуру TAP, мы организовали альтернативный процесс непрерывной сборки и предварительного тестирования.
    Одна из основных задач, стоящих перед нашими инженерами, заключается в поиске способа запуска и повтора нестандартного теста.
    Мы постарались организовать тестирование так, чтобы дать инженерам хорошо знакомый способ запуска тестов. Наша инфраструктура тестирования перед от- правкой предлагает общий API для выполнения больших тестов и тестов TAP, а наша инфраструктура обзора кода отображает оба набора результатов. Но многие большие тесты делаются на заказ, и к ним прилагается специальная документация, описывающая требования к их выполнению. Это обстоятельство может поставить в тупик инженеров, не знакомых с инфраструктурой.
    Ускорение выполнения тестов
    Инженеры не любят ждать выполнения медленных тестов. Чем медленнее работает тест, тем реже инженеры запускают его и тем больше времени требуется на устра- нение ошибок в случае сбоя.

    304
    Глава 14. Крупномасштабное тестирование
    Лучший способ ускорить тест — сузить область его охвата или разбить его на тесты меньшего размера, которые будут выполняться параллельно. Но есть и другие при- емы, помогающие ускорить большие тесты.
    Некоторые наивные тесты используют временные задержки перед появлением не- детерминированного действия, и этот прием часто используется в больших тестах.
    Однако эти тесты не ограничивают многопоточное выполнение, поэтому мы стре- мимся к тому, чтобы тест реагировал так же, как реальные пользователи, которые не любят долго ждать. Вот некоторые возможные подходы для этого:
    y выполнять опрос в цикле для определения момента изменения состояния или появления события в течение временного окна с частотой, близкой к микросе- кундам. Этот прием можно объединить с определением тайм-аута на тот случай, если тест в течение длительного времени не достигнет стабильного состояния;
    y реализовать обработчик событий;
    y подписаться на получение уведомлений о событиях.
    Обратите внимание, что тесты, которые полагаются на задержки и тайм-ауты, нач- нут давать сбой, когда возрастет нагрузка на серверы, выполняющие эти тесты, что приведет к необходимости запускать тесты чаще, что еще больше увеличит нагрузку.
    Уменьшите задержки и тайм-ауты внутри системы
    Реальные системы обычно настраиваются с учетом топологии распределенного развертывания, но SUT можно развернуть на одном компьютере (или, по крайней мере, в кластере близко расположенных машин). Если в коде имеются жестко заданные тайм-ауты или (особенно) операторы sleep для учета задержек, их следует настроить и сократить их число при выполнении тестов.
    Оптимизируйте время сборки теста
    Один из недостатков нашего монолитного репозитория — необходимость собирать все зависимости большого теста, но у этого требования есть исключения. Если
    SUT состоит из основной части, которую проверяет тест, и некоторых других двоичных зависимостей, то можно использовать в тесте предварительно собран- ные версии этих двоичных зависимостей. Наша система сборки (основанная на монолитном репозитории) плохо поддерживает эту модель, но этот подход точнее соответствует продакшену, в котором разные службы имеют разные версии.
    Устранение нестабильности
    Нестабильность — серьезный недостаток для юнит-теста, который для большого теста может стать причиной его непригодности. Команда должна рассматривать устранение нестабильности в больших тестах как высокоприоритетную задачу.
    Но как устранить нестабильность из больших тестов?
    Устранение нестабильности следует начинать с сокращения области охвата теста.
    Герметичная SUT не подвержена рискам, связанным с одновременной работой не- скольких пользователей в продакшене или общей промежуточной среде. Если она

    Большие тесты и рабочий процесс разработчика
    305
    выполняется на единственной машине, то не страдает от проблем распределенного развертывания. Смягчить другие проблемы, вызванные нестабильностью, помогут тесты и другие методы, сбалансированные со скоростью выполнения тестов.
    Использование методов реактивного или событийно-ориентированного програм- мирования может не только ускорить выполнение тестов, но и устранить причины их нестабильного поведения. Для ограничения задержек по времени необходимо использовать тайм-ауты, которые можно встроить в код теста. Увеличение числа внутренних системных тайм-аутов может уменьшить нестабильность, тогда как их сокращение, наоборот, может вызвать нестабильное поведение тестов, если система проявляет недетерминированное поведение. Важно найти правильный компромисс, определяющий допустимое поведение системы для конечных пользователей (напри- мер, указать максимально допустимый тайм-аут — n секунд), и обеспечить надежную обработку нестабильного поведения при выполнении теста.
    Превышение допустимого числа внутренних системных тайм-аутов может приво- дить к сложным ошибкам. Реальные системы часто стараются оградить конечных пользователей от катастрофических сбоев за счет обработки внутренних проблем.
    Например, если Google не может показывать рекламу в течение определенного вре- мени, мы не возвращаем код ошибки
    500
    , а просто не показываем рекламу. Но для тестировщика это выглядит так, будто код, отображающий рекламу, потерял рабо- тоспособность. В таких случаях важно сделать факт сбоя очевидным и упростить настройку внутренних тайм-аутов для тестирования.
    Увеличение понятности тестов
    Сложно интегрировать в рабочий процесс инженера тест, который дает непонят- ные результаты. Даже юнит-тесты могут вызвать путаницу — когда мое изменение нарушает работу вашего теста, мне будет трудно понять причину отказа, если я не знаком с вашим кодом, — а для больших тестов такая путаница может оказаться непреодолимой. Тесты, использующие утверждения, должны давать четкий и яс- ный сигнал об успехе/неудаче и выводить понятные сообщения об ошибке, чтобы помочь инженеру определить причины отказа. Тесты, предусматривающие участие человека, такие как A/B-тесты, требуют специального обращения, чтобы инженеры не проигнорировали их на этапе отправки.
    Как это реализуется на практике? Хороший большой тест, терпящий неудачу, должен:
    Вывести сообщение, четко поясняющее причину сбоя
    Худший сценарий — когда на экран выводятся туманное сообщение об ошиб- ке, такое как
    Ошибка утверждения
    , и трассировка стека. Хорошее сообщение учитывает, что тестировщик не знает код, и ясно описывает контекст:
    В
    test_
    ReturnsOne
    Full PageOfSearchResultsForAPopularQuery ожидалось
    10
    результатов поиска,
    а получен
    1
    . Тесты производительности или A/B-тесты должны четко объяснять, что измеряется и почему результаты тестирования считаются по- дозрительными.

    306
    Глава 14. Крупномасштабное тестирование
    Минимизировать усилия, необходимые для определения основных причин несоот-
    ветствия
    Трассировка стека бесполезна для больших тестов, потому что цепочка вызо- вов может пересекать границы нескольких процессов. В таких случаях следует сгенерировать трассировку по всей цепочке вызовов или использовать инстру- менты автоматизации, которые помогут локализовать ошибку. Для этого тест должен создать какой-то артефакт. Например, в Google используется фреймворк
    Dapper (
    https://oreil.ly/FXzbv
    ), связывающий идентификатор запроса со всеми за- просами в цепочке RPC, что позволяет сопоставить все журналы, фиксирующие прохождение запроса с указанным идентификатором, и тем самым упростить исследование трассировки.
    Предоставить сведения о поддержке и контактную информацию
    Тот, кто выполняет тест, должен иметь возможность связаться с владельцами теста или с лицами, сопровождающими тест, чтобы получить помощь.
    Владение большими тестами
    Большие тесты должны иметь официальных владельцев — инженеров, которые смогут адекватно проанализировать изменения в тесте и помочь разобраться в не- удачном завершении теста. В отсутствие официального владельца:
    y инженерам будет все труднее изменять и обновлять тест;
    y устранение причин сбоев теста будет занимать все больше времени.
    И тест перестанет использоваться.
    Интеграционным тестом для компонентов в конкретном проекте должен владеть руководитель проекта. Тест, проверяющий функциональные возможности (охва- тывающие определенную бизнес-функцию, которая поддерживается множеством служб), должен принадлежать «владельцу функции»: либо инженеру-програм- мисту, ответственному за комплексную реализацию функции, либо менеджеру по продукту или «инженеру по тестированию», которому принадлежит описание бизнес-сценария. Владелец теста должен обладать полномочиями по обеспечению общей работоспособности теста, и для этого ему нужно дать необходимые инстру- менты и стимулы.
    На основе структурированной информации о владельцах можно автоматизировать тестирование. Вот некоторые наши подходы к автоматизации тестов:
    Обычная информация о владении кодом
    Часто большой тест — это отдельный артефакт кода, который находится в опре- деленном месте в кодовой базе. В этом случае мы можем использовать инфор- мацию о владельцах (глава 9), уже имеющуюся в репозитории, чтобы подсказать инструментам автоматизации, кто владеет данным конкретным тестом.

    Итоги
    307
    Аннотации к тесту
    В один тестируемый класс или модуль может быть добавлено несколько методов- тестов, у каждого из которых может быть свой владелец функции. В таких случаях мы добавляем структурированные аннотации для каждого языка, определяющие владельца теста, чтобы в случае сбоя того или иного метода определить, к кому можно обратиться за помощью.
    Заключение
    В набор тестов должны входить большие тесты, гарантирующие точное соответствие
    SUT требованиям тестирования и помогающие выявлять проблемы, которые не обнаруживаются юнит-тестами. Поскольку большие тесты сложные и медленные, необходимо позаботиться о надлежащем владении такими тестами, содержании их в актуальном состоянии и выполнении при необходимости (например, перед развер- тыванием системы в рабочей среде). Мы стараемся делать большие тесты как можно меньше (не в ущерб точности), чтобы упростить их обслуживание. Для большинства программных проектов необходимо разрабатывать комплексную стратегию тестиро- вания, определяющую риски системы, и большие тесты, помогающие их смягчить.
    Итоги
    y
    Большие тесты выявляют проблемы, которые не обнаруживаются юнит-тестами.
    y
    Большие тесты состоят из SUT, данных, действий и проверок.
    y
    Хороший программный проект включает стратегию тестирования, определяющую риски системы, и большие тесты, помогающие их смягчить.
    y
    При создании больших тестов необходимо прикладывать дополнительные усилия, чтобы тесты не создавали ненужных сложностей в рабочем процессе инженера.

    ГЛАВА 15
    Устаревание
    Автор: Хайрам Райт
    Редактор: Том Маншрек
    Обожаю дедлайны. Обожаю свист, с которым они, словно встречный ветер, проносятся мимо.
    Дуглас Адамс
    Все системы стареют. Несмотря на то что ПО является цифровой сущностью и сами биты не разрушаются, появление новых технологий, библиотек, методов, языков и других достижений приводит к устареванию существующих систем. Старые си- стемы требуют постоянного обслуживания, специфических знаний и, как правило, больше усилий при взаимодействии с ними, потому что они постепенно все сильнее отстают от окружающей экосистемы. Часто лучше избавиться от устаревших систем, чем позволять им бесконечно находиться рядом с системами, которые призваны их заменить. Но все возрастающее число устаревших систем, которые продолжают экс- плуатироваться, ясно показывает, что на практике не так просто от них отказаться. Мы называем процесс упорядоченной миграции и последующего удаления устаревших систем устареванием, или прекращением поддержки.
    Устаревание — это еще одна тема, которая в большей мере относится к программной инженерии, чем к программированию, потому что требует размышлений об управ- лении системой с течением времени. В долгосрочных программных экосистемах правильное планирование и прекращение поддержки сокращают затраты и повышают скорость разработки, устраняя избыточность и сложность, которые со временем на- капливаются в системе. С другой стороны, неправильное проведение мероприятий по выводу системы из эксплуатации может обходиться дороже бездействия. Уста- ревшие системы требуют дополнительных усилий для их сопровождения, однако их устаревание можно запланировать на этапе проектирования, чтобы потом было проще вывести их из эксплуатации и удалить. Устаревание может затрагивать как отдельные вызовы функций, так и целые программные стеки. Далее мы сосредото- чимся в основном на устаревании кода.
    Мы в Google все еще учимся, как лучше прекращать поддержку программных систем и удалять их. В этой главе вы найдете уроки, которые мы извлекли из устаревания больших и интенсивно используемых внутренних систем. Иногда вывод из эксплу-

    Почему необходимо заботиться об устаревании
    309
    атации идет как по маслу, а иногда — нет, и удаление устаревших систем остается для нас сложной задачей.
    Эта глава в основном посвящена устареванию технических систем, не предназна- ченных для взаимодействий с конечными пользователями. Да, внешний API — это лишь одна из разновидностей продукта, и у внутреннего API тоже могут быть потребители, считающие себя конечными пользователями. Многие из описанных ниже принципов применимы к устареванию общедоступного продукта, но в этой главе мы сосредоточимся на технических и политических аспектах устаревания и удалении устаревших систем, владельцы корторых имеют представление об их использовании.
    Почему необходимо заботиться об устаревании
    Обсуждение устаревания мы начнем с утверждения: код является обязательством,
    а не ценностью. В конце концов, если считать код ценностью, зачем тогда тратить время на отключение и удаление устаревших систем? Код имеет свои издержки, меньшая часть из которых приходится на процесс создания системы и большая часть — на поддержку системы в течение всего срока ее службы. Эти постоянные издержки, такие как эксплуатационные ресурсы, необходимые для поддержания работоспособности системы, и усилия по постоянному обновлению ее кодовой базы с развитием окружающих экосистем, требуют поиска компромисса между поддер- жанием работоспособности устаревшей системы и отказом от нее.
    Возраст системы сам по себе не является определяющим фактором ее устаревания.
    Система может быть спроектирована для работы в течение нескольких лет и быть образцом хорошего ПО. Например, система верстки LaTeX совершенствовалась в течение десятилетий, и хотя изменения в нее вносятся до сих пор, они немного- численны и редки. Большой возраст системы еще не означает, что она устарела.
    Устаревание в большей степени касается систем, которые явно устарели, и для них по- явилась замена с сопоставимыми функциональными возможностями. Новая система может эффективнее использовать ресурсы, иметь лучшие показатели безопасности, обладать большей устойчивостью или просто не содержать ошибок старой системы.
    Наличие двух систем для решения одной и той же задачи порой не кажется большой проблемой, но со временем затраты на их обслуживание могут существенно возрасти.
    Зависимости, использующие устаревшую систему, могут помешать пользователям применить новую систему.
    Если старой и новой системам придется взаимодействовать друг с другом, это по- требует сложного кода для выполнения преобразований. По мере развития системы могут становиться зависимыми друг от друга, что усложнит удаление одной из них.
    Новая система, которая поддерживает совместимость со старой системой, не может развиваться. Затраты на удаление старой системы могут окупиться, потому что новая система сможет развиваться быстрее.

    310
    Глава 15. Устаревание
    Несмотря на всю пользу устаревания, мы в Google понимаем, что есть определенные ограничения на объем работ по прекращению поддержки с точки зрения команд и их клиентов. Например, всем нравятся свежеуложенные дороги, но если управление коммунальным хозяйством закроет на ремонт все дороги одновременно, никто не сможет никуда поехать. Сосредоточив свои усилия, дорожные бригады могут быстрее выполнять определенные работы и не мешать транспортному сообщению. Точно так же важно тщательно выбирать проекты для прекращения поддержки и обязательно доводить процесс их удаления до конца.
    Выше мы отметили, что «код является обязательством, а не ценностью». Если это так, то почему мы потратили большую часть этой книги на обсуждение эффективных способов создания программных систем, которые могут существовать десятилетиями? Зачем тра- тить силы и время на создание большого объема кода, если в общем балансе он попадет в статью затрат?
    Код сам по себе не имеет никакой ценности — его ценность заключена в его функциональ-
    ности. Но функциональность является ценностью, только если она отвечает потребностям пользователя: код, реализующий необходимую функциональность, является просто сред- ством достижения цели. Если ту же функциональность можно получить, написав одну строку простого и понятного кода вместо 10 000 строк сложного и запутанного кода, мы бы предпочли первый вариант. Сам код несет только затраты — чем он проще при том же объеме функциональности, тем лучше.
    Мы должны сосредоточиться не на объеме кода, который можем написать, и не на раз- мерах кодовой базы, а на максимизации количества функциональных возможностей, приходящихся на единицу кода. Для этого проще всего не писать больше кода в надежде получить больше функциональности, а удалять лишний код и системы, которые уже не нужны. Политика и процедуры прекращения поддержки кода позволяют нам работать в этом направлении.
    Почему устаревание вызывает такие сложности?
    Применим закон Хайрама к устареванию: чем больше пользователей системы, тем выше вероятность, что они будут использовать систему неожиданными и непред- виденными способами, и тем сложнее будет прекратить ее поддержку. Необычные и непредвиденные сценарии использования появляются, потому что система «слу- чайно дает нужный результат», а не «гарантирует» его. В этом контексте удаление системы можно рассматривать как бесповоротное изменение: мы не меняем поведе- ние, а полностью удаляем его! Такое радикальное изменение позволит избавиться от целого ряда неожиданных зависимостей.
    Однако отказ от развития системы обычно невозможен, пока не будет доступна новая система с той же (или лучшей!) функциональностью. Новая система может быть лучше, но она также имеет отличия от старой системы: в конце концов, если бы она в точности повторяла устаревшую систему, то не принесла бы пользователям никакой выгоды от перехода на нее (хотя могла бы принести выгоду команде, эксплуатиру-

    Почему устаревание вызывает такие сложности?
    311
    ющей ее). Такое функциональное отличие означает, что полное соответствие между старой и новой системами встречается редко и каждый сценарий использования старой системы должен оцениваться в контексте новой.
    Причиной неприятия отказа от старой системы может стать эмоциональная привя- занность к ней, особенно если принимающий решение о прекращении поддержки системы сам приложил руку к ее созданию. В Google мы иногда сталкиваемся с ар- гументом: «Мне нравится этот код!», когда пытаемся убедить инженеров сломать что-то, что они строили годами. Это понятный аргумент, но он ведет к самораз- рушению: если система устарела, она несет одни лишь издержки для организации и должна быть удалена. Один из способов решить проблему сохранения удаленного кода в Google — возможность поиска его хронологической записи (глава 17).
    В Google есть старая шутка о том, что всегда есть два способа сделать что-то: устаревший и тот, что еще не готов. Она отражает печальную реальность работы в сложной и быстро- развивающейся технологической среде, где новое решение всегда «почти» готово.
    Инженеры в Google уже привыкли работать в этой среде, но даже им требуется обратить- ся к документации, указателям и экспертам, чтобы определить, использовать ли старую систему со всеми ее недостатками или перейти на новую со всеми ее неопределенностями.
    Наконец, выделение средств и выполнение работ по удалению устаревших систем могут сопровождаться политическими сложностями. Комплектование команды и трата времени на удаление устаревших систем стоят реальных денег, в то время как затраты на бездействие и оставление системы без присмотра трудно заметить и измерить. Также может быть трудно убедить заинтересованные стороны, что работы по остановке развития и удалению системы необходимы для разработки новых функций. Методы исследования, подобные описанным в главе 7, могут по- мочь найти конкретные доказательства того, что прекращение поддержки системы имеет смысл.
    Учитывая трудности, связанные с прекращением поддержки и удалением устаревших программных систем, пользователям зачастую проще развивать систему на месте, чем полностью ее заменять. Такое развитие не останавливает процесс устаревания, а разбивает его на более мелкие и управляемые этапы, что может принести допол- нительные выгоды. Мы в Google имели возможность убедиться, насколько дорого обходится миграция на совершенно новые системы, и эта дороговизна часто недо- оценивается. Постепенный отказ от устаревшей системы с помощью рефакторинга на месте может поддерживать работоспособность существующих систем, помогая пользователям извлекать дополнительную выгоду.
    Планирование прекращения поддержки системы на этапе ее проектирования
    Прекращение поддержки программной системы можно запланировать на этапе ее создания. Выбор языка программирования, архитектуры ПО, состава команды

    312
    Глава 15. Устаревание и даже политики и культуры компании — все это определяет, насколько легко уда- лять системы в этой компании.
    Идея проектировать системы так, чтобы впоследствии их можно было объявить устаревшими, возможно, выглядит слишком радикальной, но она получила широкое распространение в других инженерных дисциплинах. Рассмотрим, например, атом- ную электростанцию, которая представляет собой сложнейшее инженерное соору- жение. При ее проектировании необходимо учитывать ее вывод из эксплуатации по истечении срока службы вплоть до выделения средств на эти цели
    1
    . Необходимость вывода атомной электростанции из эксплуатации существенно влияет на многие решения, принимаемые инженерами.
    К сожалению, программные системы редко проектируются настолько же тщательно.
    Инженеров-программистов больше заботят создание и запуск новых систем, а не об- служивание существующих. Корпоративная культура многих компаний, в том числе
    Google, ориентируется на быстрое создание и ввод в эксплуатацию новых продуктов, что часто мешает учитывать возможность прекращения поддержки продукта при его проектировании. И несмотря на популярное представление о программистах как о роботах, управляющих данными, психологически сложно планировать возможную гибель своих творений, над созданием которых мы так усердно трудимся.
    Итак, что следует учитывать при разработке систем, чтобы в будущем их проще было объявить устаревшими? Вот пара вопросов из числа тех, которые мы в Google рекомендуем рассматривать своим командам инженеров:
    y
    Насколько легко потребители смогут мигрировать с нашего продукта на его по- тенциальную замену?
    y
    Есть ли возможность постепенно заменять компоненты нашей системы?
    Многие из этих вопросов касаются особенностей предоставления услуг системой и использования зависимостей. Подробнее о порядке управления зависимостями — в главе 16.
    Отметим, что решение о долгосрочной поддержке проекта принимается, когда организация только решает его запустить. После создания программной системы остаются только три возможных варианта: поддерживать ее, максимально осторожно вывести из эксплуатации или позволить ей самой прекратить работу, когда какое- то внешнее событие сделает ее неработоспособной. Все эти варианты допустимы, и выбор любого из них будет зависеть от организации. Новый стартап с одним проектом спокойно уничтожит свою систему, когда компания обанкротится, но крупные компании должны бережнее относиться к своим инвестициям и репутации при рассмотрении возможности удаления старых проектов. Как упоминалось выше, мы в Google все еще учимся, как лучше прекращать поддержку наших внутренних и внешних продуктов.
    1
    «Design and Construction of Nuclear Power Plants to Facilitate Decommissioning», (https://
    oreil.ly/heo5Q) Technical Reports Series No. 382, IAEA, Vienna (1997).

    Подходы к прекращению поддержки
    313
    Не стоит начинать проект, который организация не намерена поддерживать в течение ожидаемого срока своей работы. Даже если организация решит отказаться от проекта и удалить его, она все равно понесет какие-то затраты, но их можно уменьшить за счет тщательного планирования и инвестиций в инструменты и политику.
    Подходы к прекращению поддержки
    Прекращение поддержки — это не один, а целая совокупность процессов, основанных на решениях, от «возможно, мы когда-нибудь отключим эту систему» до «эта система остановится завтра, и клиентам лучше быть готовыми к этому». В целом мы делим эту совокупность на две отдельные группы: рекомендуемая и принудительная миграции.
    Рекомендуемая миграция
    Рекомендуемая миграция имеет место, когда для миграции не назначен крайний срок, и вывод из эксплуатации не является приоритетной задачей для организации
    (и когда компания не желает продолжать выделять ресурсы). Такой подход к прекра- щению поддержки также можно назвать желательной миграцией: инженеры знают, что для системы есть замена, и хотя они понимают, что клиенты рано или поздно мигрируют, инженеры не намерены в ближайшем будущем оказывать клиентам помощь в миграции или удалять старую систему. В этом подходе к прекращению поддержки отсутствует элемент принуждения: мы верим, что клиенты перейдут на новую систему, но не можем заставить их сделать это. Наши друзья, занимающиеся проблемами надежности, ответят: «Надежда — это не стратегия».
    Рекомендуемая миграция — хороший инструмент для рекламы новой системы и стимулирования пользователей, которые первыми начнут ее применять. Однако новая система не должна продвигаться в период бета-тестирования: она должна быть готова к использованию, нагрузкам и поддержке новых пользователей в течение не- определенного срока. Конечно, любая новая система будет испытывать трудности роста, но когда старая система окончательно устареет, новая система станет важной частью инфраструктуры организации.
    Мы в Google наблюдали сценарий, когда рекомендованный переход на новую систему давал очевидные преимущества. В этих случаях простое уведомление пользователей о существовании новой системы и предоставление им инструментов, упрощающих миграцию, способствовали продвижению новой системы. Однако выгоды должны быть существенными. Пользователи будут неохотно мигрировать на новую систему для получения незначительных выгод, и даже новые системы со значительными улучшениями не получат полного признания, если использовать только рекомен- дательные меры по миграции.
    Рекомендуя произвести миграцию, авторы подтолкнут пользователей в желаемом направлении, но не думайте, что авторы выполнят основную работу, связанную с миграцией. Часто у автора возникает соблазн просто объявить о прекращении под-

    314
    Глава 15. Устаревание держки старой системы и уйти, не прикладывая никаких дополнительных усилий.
    Это может (немного) уменьшить количество новых применений устаревшей системы, но редко приводит к тому, что команды активно отказываются от нее. Существующие варианты использования старой системы создают своего рода концептуальное (или техническое) притяжение к ней и поглощают большую долю новых ее вариантов, сколько бы мы ни говорили: «Пожалуйста, используйте новую систему». Старая система по-прежнему будет требовать обслуживания и ресурсов, если активно не стимулировать ее пользователей к миграции.
    Принудительная миграция
    Активное стимулирование пользователей перейти на новую систему называется
    принудительной миграцией. При ней назначается крайний срок удаления устарев- шей системы, и если пользователи продолжат зависеть от нее после этой даты, они обнаружат, что их собственные системы перестали работать.
    Как это ни парадоксально, но лучший способ масштабирования принудительной ми- грации — это ее локализация в пределах одной команды экспертов, ответственной за полное удаление устаревшей системы. У этой команды есть стимул помогать другим командам мигрировать, накопленный опыт и возможность предлагать инструменты миграции. Многие миграции можно выполнить с применением инструментов, ко- торые мы обсудим в главе 22.
    Принудительная миграция должна содержать план исполнения. Это не означает, что планы не могут измениться, но такой подход дает возможность команде, осу- ществляющей процесс миграции, отключать несогласных пользователей после того, как они будут предупреждены о необходимости миграции. Без этого плана команды клиентов могут просто проигнорировать предупреждение, предпочтя потратить вре- мя не на миграцию, а на разработку новых особенностей или на выполнение другой более важной работы.
    В то же время принуждение к миграции без привлечения персонала, оказывающего необходимую помощь, может показаться клиентам злонамеренным действием, что обычно препятствует завершению процесса прекращения поддержки старой систе- мы. Клиенты могут рассматривать прекращение поддержки системы как нарушение договоренностей, требующее от них отложить решение своих приоритетных задач и заняться другой работой ради сохранения работоспособности служб. Это очень похоже на «бег на месте» и создает трения между специалистами, обслуживающими инфраструктуру, и их клиентами. Поэтому мы настоятельно рекомендуем активно поддерживать принудительное прекращение поддержки системы силами специ- ализированной команды.
    Также стоит отметить, что даже с учетом имеющейся власти принудительное прекра- щение поддержки может столкнуться с политическими препятствиями. Представьте, что вы пытаетесь принудить к миграции последнего оставшегося пользователя старой системы, от которого зависит инфраструктура всей вашей организации. Готовы ли вы

    Подходы к прекращению поддержки
    315
    разрушить эту инфраструктуру и опосредованно оказать негативное воздействие на всех, кто от нее зависит, только для того, чтобы установить произвольно выбранный крайний срок? Вряд ли отказ от поддержки действительно произойдет, если этот клиент наложит вето.
    Монолитный репозиторий и граф зависимостей в Google дают полное представ- ление об использовании систем в нашей организации. Но все равно некоторые команды могут не знать, что зависят от устаревшей системы, а обнаружение этих зависимостей аналитическим путем может быть затруднено. Такие зависимости также можно выявить динамически с помощью тестов, увеличивая частоту и про- должительность временного отключения старой системы, которое покажет, что ломается при отключении, и предупредит команды о необходимости подготовиться к приближающемуся сроку миграции. Мы в Google время от времени меняем на- звания символов, предназначенных только для реализации, чтобы узнать, какие пользователи зависят от них.
    В Google команда предупреждает о запланированных отключениях за месяцы и не- дели. Подобно учениям DiRT, эти предупреждения помогают обнаружить неизвест- ные зависимости между работающими системами. Поэтапный подход к миграции позволяет командам запланировать изменения, связанные с возможным удалением системы, или согласовать с командой, обеспечивающей миграцию, дату прекращения поддержки. (Тот же подход применим и к выявлению статических зависимостей в коде, но семантической информации, предоставляемой инструментами стати- ческого анализа, часто бывает достаточно для обнаружения всех зависимостей от устаревшей системы.)
    Предупреждение о прекращении поддержки
    Как при рекомендуемой, так и при принудительной миграции часто бывает полезно отметить системы как устаревшие, чтобы предупредить пользователей о прекраще- нии поддержки старой системы и подтолкнуть их к миграции на новую систему. Вы можете просто отметить продукт как устаревший и надеяться, что со временем он выйдет из употребления, но помните: «Надежда — это не стратегия». Предупрежде- ния об устаревании могут помочь предотвратить использование устаревшей системы в новых продуктах, но редко приводят к миграции существующих систем.
    Такие предупреждения имеют свойство накапливаться. Если они генерируются в промежуточных зависимостях (например, библиотека A зависит от библиотеки B, которая зависит от библиотеки C, и C генерирует предупреждение, которое появля- ется при сборке A), то могут быстро потерять актуальность и настанет момент, когда пользователи начнут их игнорировать. В сфере медицины эту реакцию называют
    «усталостью от предупреждений» (
    https://oreil.ly/uYYef
    ).
    Любое предупреждение о прекращении поддержки должно обладать двумя свой- ствами: действенностью и релевантностью. Под действенностью подразумевается, что любой инженер сможет, опираясь на предупреждение, предпринять на практике

    316
    Глава 15. Устаревание конкретное действие. Например, инструмент может предупредить, что вызов данной функции следует заменить вызовом более нового аналога, или в электронном письме могут быть описаны шаги по перемещению данных из старой системы в новую. В каж- дом случае предупреждение должно содержать последовательность шагов, которые инженер сможет выполнить, чтобы больше не зависеть от устаревшей системы
    1
    Предупреждение может быть действенным и при этом раздражающим. Для боль- шей пользы предупреждение должно быть еще и релевантным, то есть появляться в тот момент, когда пользователь выполняет действие, имеющее прямое отношение к устаревшей зависимости. Предупреждение об использовании устаревшей функции лучше всего выводить во время написания кода, когда предпринимается попытка использовать ее, а не спустя несколько недель после отправки зависимого кода в ре- позиторий. Точно так же электронное письмо с инструкциями по переносу данных лучше всего отправлять за несколько месяцев до удаления старой системы, а не перед выходными, после которых система будет удалена.
    Важно не поддаваться стремлению предупреждать об устаревании всего на све- те. Сами по себе предупреждения — это неплохо, но наивные инструменты часто генерируют такой поток предупреждающих сообщений, что он может ошеломить ничего не подозревающего инженера. Мы в Google очень либерально относимся к объявлению старых функций устаревшими, но используем такие инструменты, как ErrorProne (
    https://errorprone.info
    ) или clang-tidy, чтобы гарантировать целена- правленное отображение предупреждений. Как обсуждается в главе 20, мы выводим предупреждения только в новых строках кода, чтобы предупредить сотрудников, что в новом коде не следует использовать устаревший символ. Более назойливые предупреждения, например сообщающие об устаревших элементах в графе зависи- мостей, добавляются только в процессе принудительной миграции. В любом случае инструменты играют важную роль в предоставлении соответствующей информации соответствующим сотрудникам в нужное время и позволяют добавлять больше преду преждений и не утомлять получателей.
    Управление процессом прекращения поддержки
    С точки зрения управления и выполнения проекты по планомерному прекращению поддержки похожи на любые другие проекты программной инженерии, несмотря на то что могут восприниматься как мероприятия совсем другого типа. Мы не будем тратить время на выявление сходства в управлении разными проектами и поговорим в основном об особенностях прекращения поддержки.
    Владение процессом
    Мы в Google поняли, что без явных владельцев процесс прекращения поддержки вряд ли достигнет значительного прогресса, сколько бы предупреждений не гене-
    1
    См. пример на https://abseil.io/docs/cpp/tools/api-upgrades

    Управление процессом прекращения поддержки
    317
    рировала система. Наличие явных владельцев проекта, управляющих процессом прекращения поддержки, может показаться неэффективной тратой ресурсов, но еще дороже не завершать прекращение поддержки или делегировать усилия по миграции пользователям системы. Во втором случае мы получим рекомендуемую миграцию, которая никогда не завершится естественным путем, а в первом возьмем на себя обязательство продолжать поддерживать старые системы. Централизация усилий по прекращению поддержки, как показывает опыт, помогает гарантировать
    снижение затрат на миграцию, делая их более прозрачными.
    Заброшенные проекты часто превращаются в проблему, когда возникает необхо- димость установить их владельца и согласовать с ним меры по стимулированию к миграции. В каждой организации разумного размера есть проекты, которые все еще активно используются, но ими явно никто не владеет и никто их не поддержи- вает. Иногда проекты оказываются в этом состоянии из-за того, что они устарели: первоначальные владельцы перешли в проект-преемник, оставив устаревший проект в качестве зависимости другого проекта в надежде, что он со временем исчезнет.
    Такие проекты редко исчезают сами по себе. Как показывает наш опыт, несмотря на самые радужные надежды, для таких проектов по-прежнему требуется участие специалистов по прекращению поддержки и выводу их из эксплуатации. Удаление должно быть основной целью отдельной команды, поскольку при наличии других задач работа по прекращению поддержки почти всегда будет восприниматься коман- дой как низкоприоритетная и редко будет получать необходимое внимание. Важные, но не срочные задачи по очистке требуют 20 % рабочего времени и предоставляют инженерам доступ к другим частям кодовой базы.
    Основные этапы
    При создании новой системы основные этапы проекта, как правило, предельно ясны, например: «Запустить функцию frobnazzer к следующему кварталу».
    Следуя практике последовательной разработки, команды постепенно создают и предоставляют новые функциональные возможности пользователям, которые выигрывают от каждой новой возможности. Конечной целью может быть запуск всей системы, но последовательные этапы помогают команде почувствовать про- гресс и избавляют от необходимости ждать окончания процесса, чтобы принести пользу организации.
    Напротив, единственным важным этапом процесса прекращения поддержки часто может считаться полное удаление устаревшей системы. Команда может чувствовать, что ничего не добилась, пока не выключит свет и не уйдет домой. Конечно, удаление системы — значимый шаг для команды, но вне команды его никто не заметит, потому что к этому моменту у системы уже нет пользователей. Руководители проектов по прекращению поддержки не должны поддаваться искушению сделать факт удаления системы единственной измеримой вехой в работе команды, особенно если учесть, что иногда оно не требуется.

    318
    Глава 15. Устаревание
    Так же как создание новой системы, прекращение поддержки старой системы должно предусматривать конкретные этапы, полезность которых можно оценить. Метрики для оценки прогресса в направлении прекращения поддержки могут быть специ- фическими, но для поддержания морального духа в команде, как и в любом про- екте, важно отмечать промежуточные достижения, такие как удаление ключевого субкомпонента и т. п.
    Инструменты для миграции
    Большая часть инструментов для управления процессом прекращения поддержки подробно обсуждается в других главах этой книги, например инструменты для про- цесса крупномасштабных изменений (глава 22) и для процесса обзора кода (глава 19).
    Поэтому мы не будем подробно обсуждать особенности инструментов здесь, а лишь кратко опишем, как они могут пригодиться при управлении прекращением поддержки устаревшей системы. Их можно разделить на инструменты обнаружения, миграции и предотвращения отката.
    Обнаружение
    На ранних этапах процесса прекращения поддержки, а точнее на всем его про- тяжении, полезно знать, как и кем используется устаревшая система. Основная начальная работа по прекращению поддержки заключается в определении, кто использует старую систему и какими неожиданными способами. В зависимости от видов использования и после получения новой информации мы можем пересмотреть решение о прекращении поддержки. Кроме того, мы используем эти инструменты, чтобы контролировать работу.
    Такие инструменты, как Code Search (глава 17) и Kythe (глава 23), позволяют статически определить, какие клиенты и как используют ту или иную библиотеку, и показывают, от какого неожиданного поведения клиенты зависят. Поскольку зави- симости времени выполнения обычно требуют использования некоторой статической библиотеки или тонкого клиента, этот метод позволяет получить большую часть информации, необходимой для запуска и выполнения процесса прекращения под- держки. Журналирование и телеметрия среды выполнения в продакшене помогают обнаружить проблемы, связанные с динамическими зависимостями.
    Наконец, наш глобальный набор тестов, как оракул, показывает, все ли ссылки на старый символ были удалены. Как обсуждалось в главе 11, тесты — это механизм предотвращения нежелательных изменений в поведении системы по мере развития организации. Прекращение поддержки является важной частью этого развития, и клиенты несут ответственность за достаточность тестов, гарантирующих, что удаление устаревшей системы не нанесет им вреда.
    Миграция
    Большая часть работы, связанной с прекращением поддержки, в Google выполняется с применением набора инструментов, который используется для генерации и обзора

    Заключение
    319
    кода, упоминавшегося выше. Процесс крупномасштабных изменений и его инстру- менты особенно полезны при масштабном обновлении кодовой базы для включения ссылок на новые библиотеки или службы времени выполнения.
    Предотвращение отката
    Наконец, часто упускаемый из виду элемент инфраструктуры прекращения под- держки — это инструменты, препятствующие использованию в новом коде объектов, которые находятся на пути к удалению. Даже в случае рекомендуемой миграции полезно предупредить пользователей о том, что при написании нового кода вместо устаревшей системы они должны использовать новую. Без предотвращения отката процесс прекращения поддержки может превратиться в бесконечную игру, в которой пользователи постоянно добавляют новые варианты использования знакомой им системы (или находят примеры такого использования в кодовой базе), а команда, занимающаяся прекращением поддержки, постоянно переносит эти новые вариан- ты использования в новую систему. Это контрпродуктивный и деморализующий процесс.
    Чтобы предотвратить откат на микроуровне, мы используем фреймворк статическо- го анализа Tricorder, который уведомляет пользователей о том, что они добавляют обращения к устаревшей системе, и предлагает соответствующую замену. Владель- цы устаревших систем могут добавлять аннотации компилятора к устаревшим символам (например,
    @deprecated в Java), а Tricorder будет обнаруживать попытки использования устаревших символов в новом коде во время проверки. В ограничен- ных случаях инструмент также предлагает исправления, которые можно применить щелчком мыши.
    На макроуровне, чтобы предотвратить появление новых зависимостей от устаревшей системы, мы используем белые списки в нашей системе сборки. Автоматизированные инструменты периодически проверяют эти белые списки и корректируют их по мере миграции зависящих систем.
    Заключение
    Прекращение поддержки в чем-то напоминает грязную работу по уборке улицы после того, как по ней прошел цирковой парад, но эти усилия улучшают общую про- граммную экосистему, сокращая накладные расходы на обслуживание и уменьшая когнитивную нагрузку на инженеров. Масштабируемое обслуживание сложных программных систем — это больше, чем просто создание и запуск ПО: оно подраз- умевает возможность удаления устаревших или неиспользуемых систем.
    Полный процесс прекращения поддержки включает успешное решение социальных и технических проблем с помощью политических мероприятий и инструментов.
    Организованное и управляемое прекращение поддержки часто не рассматривается как источник выгоды для организации, но оно необходимо для ее устойчивого раз- вития в долгосрочной перспективе.

    320
    Глава 15. Устаревание
    Итоги
    y
    Программные системы требуют постоянных затрат на обслуживание, которые следует сопоставлять с затратами на их удаление.
    y
    Удалить систему часто труднее, чем создать ее с нуля, потому что пользователи нередко используют систему непредвиденными способами.
    y
    Развитие существующей системы обычно обходится дешевле, чем ее замена на новую систему, если учитывать затраты на прекращение поддержки.
    y
    Трудно достоверно оценить затраты на прекращение поддержки: помимо прямых затрат на обслуживание, связанных с сохранением старой системы, существуют экосистемные затраты, связанные с наличием множества аналогичных систем, между которыми нужно организовать взаимодействие. Старая система может неявно тормозить разработку новых возможностей.

    1   ...   35   36   37   38   39   40   41   42   ...   69


    написать администратору сайта