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

  • SemVer может слишком много обещать

  • Выбор минимальной версии

  • То есть SemVer работает

  • Управление зависимостями с бесконечными ресурсами

  • Делай как вGoogle


    Скачать 5.77 Mb.
    НазваниеДелай как вGoogle
    Дата31.05.2022
    Размер5.77 Mb.
    Формат файлаpdf
    Имя файлаDelay_kak_v_Google_Razrabotka_programmnogo_obespechenia_2021_Tom.pdf
    ТипДокументы
    #559735
    страница56 из 69
    1   ...   52   53   54   55   56   57   58   59   ...   69
    SemVer может чрезмерно ограничивать
    Рассмотрим ситуацию, когда libbase не является единым монолитом и почти во всех ее библиотеках есть независимые интерфейсы. Даже если в ней всего две функции, легко представить случаи, в которых SemVer будет накладывать на нее чрезмерные ограничения. Допустим, что libbase действительно состоит всего из двух функций:
    Foo и
    Bar
    . Зависимости среднего уровня liba и libb используют только
    Foo
    . Если разработчик libbase внесет важное изменение в
    Bar
    , то, следуя правилам SemVer, он должен будет увеличить старший номер версии библиотеки. Известно, что liba и libb зависят от libbase
    1.x
    , и решатели зависимостей на основе SemVer не примут версию
    2.x этой зависимости, хотя в действительности эти библиотеки прекрасно работали бы вместе: изменилась только функция
    Bar
    , которую liba и libb не исполь- зуют. Требование «после добавления несовместимого изменения следует увеличить старший номер версии» влечет потери, когда применяется не на уровне детализации отдельной атомарной единицы API. Некоторые зависимости могут быть достаточно дробными, чтобы изменение номера версии точно отражало ситуацию
    1
    , но в целом это не является нормой для экосистемы SemVer.
    Чрезмерное ограничение, накладываемое SemVer и обусловленное неоправданно большим скачком или недостаточно высокой точностью номеров версий, вынуждает диспетчеров пакетов и SAT-решателей сообщать, что зависимости нельзя обновить или установить, даже если они будут точно совместимы. Любой сталкивавшийся с адом зависимостей во время обновления скажет, насколько сильно его раздражает, что большая часть усилий тратится впустую.
    SemVer может слишком много обещать
    С другой стороны, SemVer заставляет предположить, что оценка совместимости API, сделанная его разработчиком, достаточно точная и что изменения делятся на три группы: нарушающие совместимость (путем изменения или удаления компонентов), добавляющие новые возможности и не влияющие на API. Если считать, что SemVer точно отражает риск изменения путем классификации синтаксических и семанти- ческих изменений, то как охарактеризовать изменение, которое добавляет задержку в одну миллисекунду в API, чувствительный ко времени? Как охарактеризовать из- менение, меняющее формат записей, выводимых в журнал, порядок импорта внешних зависимостей или порядок результатов, возвращаемых в «неупорядоченном» потоке?
    1
    В экосистеме Node есть достойные внимания примеры зависимостей, которые предоставляют ровно один API.

    440
    Глава 21. Управление зависимостями
    Разумно ли предполагать, что изменения «безопасны» только потому, что они не являются частью синтаксиса или контракта API? А если в документации сказано:
    «API может измениться в будущем»? Или API имеет имя «ForInternalUseByLib-
    BaseOnlyDoNotTouchThisIReallyMeanIt» (ТолькоДляВнутреннегоИсполь зо ва- нияВLibBaseНеМенятьЯСерьезно)
    1
    ?
    Идея о том, что исправления в SemVer, в которых изменены только детали реали- зации, являются «безопасными», абсолютно противоречит опыту компании Google с ее законом Хайрама: «При достаточно большом количестве пользователей каждое наблюдаемое поведение системы будет зависеть от кого-то из них». Изменение по- рядка импортирования зависимостей или порядка результатов в «неупорядоченном» потоке в большом масштабе неизбежно нарушит предположения, на которые (воз- можно, ошибочно) опирался пользователь API. Сам термин «критическое измене- ние» вводит в заблуждение: есть изменения, которые теоретически несовместимы, но безопасны на практике (например, удаление неиспользуемого API). Существуют также изменения, которые теоретически безопасны, но на практике нарушают рабо- тоспособность клиентского кода (любой из предыдущих примеров действия закона
    Хайрама). Это можно увидеть в любой системе, использующей SemVer для управ- ления зависимостями, в которой система требований к номеру версии допускает ограничения на номер исправления: если можно сказать «
    liba требует libbase
    >1.1.14
    » вместо «
    liba требует libbase
    1.1
    », это явно указывает на то, что имеются заметные раз- личия в версиях, отличающихся только номером исправления.
    Изменение само по себе не является нарушающим или не нарушающим совмести-
    мость. Фраза: «Это изменение нарушает совместимость» требует уточнения, для какого (известного или неизвестного) круга пользователей и для каких вариантов использования. Но реальная оценка изменения фактически зависит от ответа на вопрос, которого нет в формулировке SemVer: как пользователи используют за- висимость?
    Поэтому решатель ограничений SemVer может сообщить, что зависимости совме- стимы, когда в действительности они не совместимы, потому что увеличение версии было произведено неправильно или что-то в сети зависимостей, согласно закону
    Хайрама, зависит от чего-то, что не является частью наблюдаемой поверхности API.
    В этих случаях могут возникать ошибки во время сборки или выполнения с неопре- деленной верхней границей ущерба.
    Мотивация
    Есть еще один аргумент, согласно которому SemVer может мешать разработчикам создавать стабильный код. На разработчика произвольной зависимости действует, в той или иной степени, системный стимул — не добавлять критических изменений
    1
    Как показывает наш опыт, такое именование не решает проблему доступности закрытых
    API для пользователей. Для этой цели лучше использовать языки, предлагающие более надежные средства управления доступностью публичных и приватных API.

    Ограничения SemVer
    441
    и не увеличивать старший номер версии. Некоторые проекты уделяют большое вни- мание совместимости и стараются сделать все возможное, чтобы избежать проблем с изменением старшего номера версии. Другие, напротив, действуют агрессивно и по расписанию намеренно выпускают версии с увеличенным старшим номером.
    Проблема в том, что у большинства пользователей зависимости нет веских причин интересоваться предстоящими изменениями, они не подписываются на рассылки или другие уведомления о выпуске новых версий.
    Таким образом, независимо от количества пользователей, которые столкнутся с неудобствами из-за несовместимого изменения в популярном API, на долю его разработчика приходится лишь крошечная часть затрат, вызванных повышением версии. Разработчики, одновременно являющиеся пользователями, могут иметь стимул к нарушению совместимости: улучшенный интерфейс проще разрабатывать, когда отсутствуют устаревшие ограничения. Это одна из причин, почему мы считаем, что проекты должны публиковать свои намерения в отношении совместимости, по- рядка использования и критических изменений. Даже если изменения реализованы с максимальной эффективностью, не являются обязательными или игнорируются многими пользователями, сообщения о них все равно помогают оценить, является ли это изменение «стоящим», без привлечения конфликтующих стимулов.
    Go (
    https://research.swtch.com/vgo-import
    ) и Clojure (
    https://oreil.ly/Iq9f_
    ) прекрасно справляются с информированием: в их стандартных экосистемах управления па- кетами с увеличением старшего номера версии создается совершенно новый пакет.
    Согласитесь, если вы решили нарушить обратную совместимость для своего пакета, зачем делать вид, что это тот же набор API? Переупаковка и переименование явно сообщают, что поставщик решил отказаться от обратной совместимости.
    И наконец, свою лепту в управление зависимостями вносит человеческий фактор.
    В общем случае, если следовать правилам SemVer, семантические изменения, на- равне с синтаксическими, тоже должны приводить к увеличению версии, поскольку изменение поведения API имеет такое же значение, как и изменение его структуры.
    Теоретически можно разработать инструменты для оценки наличия синтаксических изменений в конкретной версии набора общедоступных API, но оценить наличие значимых и преднамеренных семантических изменений вычислительными сред- ствами не представляется возможным
    1
    . С практической точки зрения даже потен- циально возможные инструменты для выявления синтаксических изменений имеют серьезные ограничения. Почти во всех случаях решение об увеличении старшего или младшего номера версии или номера исправления зависит от разработчика
    API. Если вы используете только несколько зависимостей, поддерживаемых на вы- соком профессиональном уровне, то вероятность столкнуться с ошибкой инженера,
    1
    В мире вездесущих юнит-тестов мы могли бы идентифицировать изменения, требующие изменить поведение теста, но все равно было бы сложно алгоритмически отделить «из- менение поведения» от «исправления ошибки в поведении, которое не было задумано или обещано».

    442
    Глава 21. Управление зависимостями скорее всего, будет невысока
    1
    . Но если вы используете сеть из тысяч зависимостей, то будьте готовы встретиться с некоторым беспорядком, обусловленным простыми ошибками разработчика.
    Выбор минимальной версии
    В 2018 году в серии статей о создании системы управления пакетами для языка программирования Go Расс Кокс описал интересный вариант семантического управления зависимостями — выбор минимальной версии (MVS, minimum version selection) (
    https://research.swtch.com/vgo-mvs
    ). После обновления версии узла в сети зависимостей может потребоваться обновить его зависимости до более новых версий, чтобы удовлетворить изменившееся требование. Это, в свою очередь, может вызвать дальнейшие изменения. В большинстве формулировок удовлетворения ограничений и выбора версии предлагается выбирать самые свежие версии нижестоящих зависи- мостей. В конце концов, рано или поздно все равно придется обновить зависимости до этих версий, верно?
    Метод MVS предлагает делать противоположное: если спецификация liba требует libbase
    ≥1.7
    , то нужно попробовать ограничиться обновлением libbase до версии
    1.7
    , даже если доступна версия
    1.8
    . Это «создает высокоточные сборки, в которых за- висимости, создаваемые пользователем, максимально приближены к использовав- шимся автором»
    2
    . Это утверждение раскрывает критически важную истину: когда liba требует libbase
    ≥1.7
    , это почти всегда означает, что разработчик liba использовал libbase
    1.7
    . Если предположить, что перед публикацией новой версии разработчик провел хотя бы минимальное тестирование
    3
    , то у нас есть по меньшей мере отдельные свидетельства проверки совместимости этой версии liba с версией libbase
    1.7
    . Это не доказательство, полученное системой непрерывной интеграции или полноценным юнит-тестированием, но уже кое-что.
    При отсутствии точных входных ограничений, полученных из 100%-но точного предсказания будущего, двигаться вперед лучше небольшими шагами. Мы знаем, что безопаснее отправить в репозиторий результаты работы за час, чем за год, и точно так же безопаснее двигаться небольшими шагами при обновлении зависимостей.
    Метод MVS предлагает двигаться вперед по каждой зависимости ровно настолько, насколько это необходимо, и говорит: «Итак, я продвинулся достаточно далеко, чтобы получить то, о чем вы просили (и не дальше). Почему бы вам не провести несколько тестов и не проверить, все ли в порядке?»
    В основе идеи MVS лежит понимание, что более новая версия может вызвать несо- вместимость, даже если в теории номера версий говорят об обратном. Это призна- ние главной проблемы SemVer: выражение изменений в ПО в виде номеров версий
    1
    Поэтому, когда это важно в долгосрочной перспективе, выбирайте хорошо поддерживаемые зависимости.
    2
    Cox R. Minimal Version Selection. February 21, 2018, https://research.swtch.com/vgo-mvs
    3
    Если это предположение не выполняется, то вам стоит перестать зависеть от liba

    Управление зависимостями с бесконечными ресурсами
    443
    неизбежно влечет некоторую потерю точности. Метод MVS дает дополнительную практическую точность, ограничивая выбор версий наиболее близкими к тем, со- вместимость с которыми предположительно была проверена. Этого может быть достаточно, чтобы обширная сеть зависимостей функционировала должным об- разом. К сожалению, мы не нашли хорошего способа эмпирически проверить эту идею. Пока еще нет доказательств, что MVS делает SemVer «достаточно хорошим» без решения основных теоретических проблем и проблем со стимулами, но мы по- прежнему считаем, что MVS — это явное улучшение в применении сегодняшних ограничений SemVer.
    То есть SemVer работает?
    SemVer хорошо работает в ограниченных масштабах. Однако важно понимать, что утверждает этот подход, а что — нет. SemVer будет работать нормально при условии, что:
    y поставщики ваших зависимостей точны и ответственны (и разработчики не до- пускают ошибок при назначении версий);
    y ваши зависимости достаточно дробные (чтобы не опасаться чрезмерных огра- ничений при обновлении неиспользуемых API в них и связанного с этим риска невыполнения требований SemVer);
    y все случаи использования API не выходят за пределы ожидаемого использова- ния (чтобы избежать неожиданного нарушения работоспособности из-за пред- положительно совместимого изменения напрямую или в коде, от которого вы зависите транзитивно).
    Когда в графе зависимостей есть лишь несколько тщательно отобранных и хорошо поддерживаемых зависимостей, SemVer может быть идеальным решением.
    Однако наш опыт показывает, что ни одно из этих трех свойств невозможно мас- штабировать и поддерживать в течение долгого времени. По мере разрастания сети зависимостей, как по размеру каждой зависимости, так и по их количеству (а также по любым эффектам, обусловленным использованием монолитного репозитория, в котором присутствует нескольких проектов, зависящих от одной сети внешних зависимостей), совокупная потеря точности SemVer будет только увеличиваться.
    Эти проблемы проявляются как в ложноположительных сбоях (когда теоретически совместимые версии оказываются несовместимыми на практике), так и в ложноотри- цательных (когда фактически совместимые версии отвергаются SAT-решателями, что приводит к аду зависимостей).
    Управление зависимостями с бесконечными ресурсами
    Проведем мысленный эксперимент и посмотрим, как выглядело бы управление зависимостями, если бы все мы имели доступ к неограниченным вычислительным ресурсам? На что можно было бы надеяться, если бы мы были ограничены только

    444
    Глава 21. Управление зависимостями видимостью и слабой координацией между организациями? В настоящее время
    SemVer популярен в отрасли программной инженерии по трем причинам:
    y дает только локальную информация (разработчику API не нужно знать детали проектов их пользователей);
    y не предполагает наличия тестов (пока тестирование в отрасли выполняется не повсеместно, но мы определенно придем к этому в следующем десятилетии), вычислительных ресурсов для выполнения тестов или систем непрерывной ин- теграции с целью мониторинга результатов тестирования;
    y является существующей практикой.
    «Предоставление» локальной информации на самом деле не обязательное, в частно- сти потому, что сети зависимостей, как правило, формируются только в двух средах:
    y внутри отдельных организаций;
    y внутри экосистемы открытого ПО, где исходный код доступен всем, даже если проекты не сотрудничают явно.
    В любом из этих случаев доступна важная информация об использовании зависи- мостей, даже если в настоящий момент она не раскрывается или не используется.
    То есть доминирование SemVer отчасти обусловлено тем, что мы предпочитаем иг- норировать информацию, которая теоретически доступна. Если бы у нас был доступ к большему количеству вычислительных ресурсов и информация о зависимостях была бы легкодоступна, то сообщество наверняка нашло бы ей применение.
    От пакета открытого ПО может зависеть бесчисленное множество компонентов с закрытым исходным кодом, однако обычно популярные пакеты открытого ПО популярны среди как открытого, так и закрытого ПО. Сети зависимостей не смеши- вают (и не могут) бесконтрольно открытые и закрытые зависимости: как правило, есть подмножество открытых зависимостей и отдельный закрытый подграф
    1
    Теперь вспомним намерение SemVer: «По моей оценке, это изменение будет легко (или сложно) принять». Есть ли лучший способ передать эту информацию? Да, в виде прак- тического опыта, демонстрирующего, что изменение совместимо. Как получить такой опыт? Если большая часть (или, по крайней мере, репрезентативная выборка) наших зависимостей общедоступна, каждое предлагаемое изменение будет неизменно тести- роваться. При достаточно большом количестве таких тестов у нас будет, по крайней мере, статистический аргумент в пользу безопасности изменения (по закону Хайрама).
    Если тесты по-прежнему выполняются успешно, то изменение совместимое и не имеет значения, изменило ли оно структуру API, исправило ли ошибки или сделало и то и другое — нам не нужно ни классифицировать, ни оценивать это изменение.
    Теперь представьте, что экосистема открытого ПО оказалась в мире, где изменения сопровождаются доказательствами их безопасности. Если не учитывать затраты на
    1
    Потому что сеть зависимостей в открытом ПО обычно не может зависеть от группы за- крытых узлов, разве только от встроенного закрытого ПО, управляющего графикой.

    Управление зависимостями с бесконечными ресурсами
    445
    вычисления, истинная
    1
    оценка «насколько это безопасно» будет зависеть только от выполнения соответствующих тестов в зависимых проектах.
    Даже без формальной непрерывной интеграции, применяемой ко всей экосистеме открытого ПО, мы можем использовать такой граф зависимостей и другие вторичные сигналы для более целенаправленного анализа изменений. Первостепенное внимание в таком случае должно уделяться тестам в зависимостях, которые широко использу- ются, имеют хорошую профессиональную поддержку, систематически предоставляют хороший сигнал и высококачественные результаты тестирования. Помимо особого внимания к тестам, которые могут дать больше всего экспериментальной информа- ции о качестве изменений, можно использовать информацию от авторов изменений, чтобы оценить риск и выбрать подходящую стратегию тестирования. Выполнение
    «всех соответствующих» тестов теоретически необходимо, если цель состоит в том, чтобы убедиться, что «ни одна чья-то зависимость не изменяется с нарушением со- вместимости». Если цель состоит в том, чтобы «уменьшить риск», статистический аргумент становится более привлекательным (и наименее затратным).
    В главе 12 мы определили четыре разновидности изменений: от простого рефакторин- га до изменения существующей функциональности. Учитывая модель на основе не- прерывной интеграции для обновления зависимостей, мы можем начать отображать эти разновидности изменений в модели, подобной SemVer, используя которую автор изменения оценивает риск и применяет соответствующий уровень тестирования.
    Например, простой рефакторинг, изменяющий только внутренние API, можно от- нести к категории с самым низким риском и для его проверки достаточно выполнить тесты только в нашем проекте и, возможно, в некоторых других важных проектах, напрямую зависящих от изменения. С другой стороны, изменение, которое удаляет устаревший интерфейс или изменяет наблюдаемое поведение, может потребовать столько тестирования, сколько вообще мы сможем себе позволить.
    Что необходимо изменить в экосистеме открытого ПО, чтобы применить такую модель? К сожалению, довольно много:
    y
    Все зависимости должны содержать юнит-тесты. Мы неумолимо движемся к миру, в котором юнит-тестирование будет использоваться повсеместно.
    y
    Сеть зависимостей для большей части экосистемы открытого ПО понятна.
    Но неясно, есть ли механизм для выполнения графовых алгоритмов в этой сети — информация открыта и доступна, но фактически никак не индексируется или не используется. Многие системы управления пакетами или зависимостями по- зволяют видеть зависимости проекта, но не отображают, какие проекты зависят от тех или иных зависимостей.
    y
    Объем вычислительных ресурсов, доступных для непрерывной интеграции, все еще очень ограничен. Большинство разработчиков не имеют вычислительных кластеров для сборки и тестирования.
    1
    Или близкая к истинной.

    1   ...   52   53   54   55   56   57   58   59   ...   69


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