Webrium
Подписаться

Политика обновления зависимостей, которую соблюдают

Разбираемся, почему матрицы дедлайнов и дни обновлений не работают и как построить политику, в которой обновлять зависимости дешевле, чем откладывать

Два маршрута обновления зависимостей: короткий путь откладывания обходится дорого, длинный путь регулярных обновнений — дёшевоОтложить — дорогоОбновлять — дёшево

У обновления зависимостей два режима, и их почти всегда путают. В первом ничего не случилось: вышла новая версия, можно обновиться сегодня, можно через месяц — ничего не давит. Во втором случилось: в библиотеке из вашего lock-файла нашли уязвимость, и теперь чужой график диктует ваш. Политика обновлений нужна ровно для одного: чтобы решение «обновлять или нет» не принималось каждый раз заново, по настроению и по близости релиза.

Большинство политик при этом не работает. Их пишут, кладут в вики и тихо не соблюдают — не потому, что команда безответственная, а потому, что политика обещает поведение, не меняя его стоимость. Соблюдают ту политику, в которой соблюдать дешевле, чем нарушать. Дальше — как собрать такую руками.

Тупик: матрица дедлайнов и «день обновлений»

Самый очевидный первый ход — документ с матрицей срочности: критические уязвимости закрываем за столько-то, высокие — за столько-то, остальное — раз в квартал. Второй ход той же природы — «день обновлений»: одним коммитом подтягивается всё накопившееся.

Оба ломаются об одно и то же.

Дедлайн — это обещание о будущем поведении. В момент, когда пора обновляться, всё, что делало обновление дорогим, стоит на месте: тесты не покрывают то, как вы используете библиотеку, релизная ветка заморожена, мажор тянет за собой цепочку транзитивных зависимостей. Обещание не убирает ни одного из этих препятствий — оно назначает виноватого. Дальше предсказуемо: первое нарушение, второе, исключения становятся нормой, и документ умирает молча. Отмену такой политики никто не объявляет — она просто перестаёт существовать.

У матрицы есть и вторая беда: критичность назначает автор уведомления, и назначает её среднему пользователю пакета, а не вам. «Критическая» уязвимость в ветке кода, которую вы не вызываете, конкурирует за тот же день с настоящей проблемой. Чужая шкала не переживает встречи с вашим бэклогом.

«День обновлений» ломается арифметикой партии. Обновления в партии не локализуются: сборка падает — и непонятно, какое из изменений виновато; откатывать приходится всё, потом раскатывать заново по одному. Стоимость партии растёт быстрее её размера. Чем дольше день откладывали, тем больше партия; чем больше партия, тем неохотнее за неё берутся — и тем вероятнее, что она пересекает мажорные границы, а мажор — это чтение заметок о миграции, а не нажатие кнопки.

Общий корень обоих тупиков: обновление воспринимается как событие, которое надо назначить в календаре, а не поток, который надо удешевить. Пока стоимость не изменилась, любой календарь — декорация.

Два потока: рутина и безопасность

Рабочая политика начинается с разделения потоков: у них разные триггеры, разная цена ошибки и разные процедуры решения.

Рутина. Триггер — вышла новая версия. Цель — сделать обновление скучным, а скучное — частым и мелким. Ставите бота, который следит за новыми версиями и открывает pull-реквесты; какой именно — неважно, это стандартный класс инструментов. Патчи и минорные версии идут пачкой, мажоры — по одному, с чтением заметок о миграции: стоимость мажора живёт в чтении, а не в мерже. Пачка — тоже решение: группировать стоит то, что тестируется вместе, а не всё подряд; бот, сваливающий в один реквест всё накопившееся, возвращает вас к арифметике партии.

Частое и мелкое дешевле редкого и крупного — это не дисциплина ради дисциплины, а арифметика. Чем больше прыжок между версиями, тем больше изменений вы принимаете за раз и тем труднее найти то, которое сломало поведение. Мелкий прыжок локализует проблему в крошечном диффе. Отставание к тому же копится с процентами: экосистема движется, транзитивные зависимости требуют друг от друга всё более свежих версий, и цена наверстывания растёт быстрее, чем само отставание.

Автоматический мерж — условная опция, а не догма. Включать его можно при двух выполненных условиях: тесты покрывают интеграционную поверхность — то, как вы реально используете библиотеку, а не строчки кода вообще, — и откат быстрый. Нарушено любое из них, и авто-мерж превращает красную сборку в продакшен-инцидент: это не экономия труда, а перенос того же труда в худшее место. Ручной мерж по зелёной сборке медленнее, зато без сюрпризов.

Безопасность. Триггер — уведомление об уязвимости. Политика здесь — не срок, а процедура из трёх вопросов:

  1. Есть ли пакет у нас и где он используется? На это отвечает перечень цепочки поставки — тот самый, который кто-то реально читает. Без него первый вопрос съедает часы, с ним — минуты; о том, как такой перечень устроить, у нас есть отдельный материал.
  2. Досягаем ли уязвимый путь из того, что у вас открыто наружу? Уведомление описывает конкретную ветку кода. Если вы её не вызываете или до неё не добраться снаружи — события не произошло. Проверка обычно дешёвая: из перечня видно, где живёт пакет, а дальше — вызываете ли вы ту самую функцию.
  3. Что дешевле: обновление или обход? Иногда уязвимый функционал отключается конфигурацией, и это дешевле, чем перепрыгивать через мажор под давлением.

Ответы раскладывают уведомления по потокам. Недостижимый путь — в рутину: обновитесь в порядке очереди, но обновитесь, потому что оценка досягаемости быстро устаревает. Достижимый и открытый снаружи — прерывание: обновление прыгает через очередь. Если само обновление — большой мажор, обход делается сейчас, а мажор уходит в плановый поток с датой в регистре исключений.

И про сроки. Ваш настоящий срок реакции не пишется в политике — он измеряется в конвейере. Замерьте время от решения «обновляем» до работающего продакшена для обычного патча. Это и есть честный срок; обещание в документе, которое быстрее него, сработает как ложь в первом же инциденте. Хотите обещать быстрее — укорачивайте конвейер: тесты, деплой, откат. Политика тут ни при чём. Тот же замер — честный ответ на вопрос «когда закроете», заданный в неудобный момент.

Порог отставания и видимые исключения

Поток — ещё не политика, нужна проверяемая граница. Выберите порог отставания — например, «не отстаём от последнего мажора дольше, чем на ваш обычный релизный цикл», — и главное условие: порог проверяет машина. Порог, который проверяет человек, не проверяется никто.

Калибровка без лишних цифр: смотрите, как распределяются нарушения. Кучкуются на одних и тех же зависимостях — это кандидаты в исключения. Размазаны равномерно — поток буксует: бот выключен, мержи лежат, конвейер мешает. Порог, нарушаемый постоянно, — не порог, а шум: поднимите его или почините поток, но не оставляйте мигать красным. Привыкание к красному и есть смерть политики.

Исключение — легальный способ нарушить, и у него три обязательных поля: причина, владелец, дата пересмотра. Исключение без даты — не исключение, а дрейф с подписью. Обход уязвимости из предыдущего раздела живёт здесь же: обход — это заём, а не решение, и у заёма есть срок возврата.

Порядок действий

  1. Измерьте конвейер: обновите одну рутинную зависимость, доведите до продакшена и запишите, где застряли. Не получается даже с одним патчем — у вас проблема не с политикой, а с тестами или откатом; чините сначала это.
  2. Включите бот на патчи и миноры. Авто-мерж — только при условиях из раздела выше, иначе мержите руками по зелёной сборке.
  3. Мажоры — по одному, с чтением заметок о миграции. Если мажор тянет цепочку транзитивных обновлений, это уже не рутина, а маленький проект: заведите задачу и оцените её как задачу.
  4. Пропишите процедуру для уведомлений: три вопроса и человек, отвечающий за каждый — обычно владелец перечня зависимостей и тот, кто отвечает за внешний контур.
  5. Поставьте машинный порог отставания и регистр исключений с датами.
  6. Через релизный цикл вернитесь к пункту 1 и сравните время. Падает или хотя бы не растёт — политика работает. Растёт — вы поддерживаете документ, а не поток.

Позиция редакции

Мы считаем, что политика обновления зависимостей — это конфигурация конвейера, а текст — её краткая запись: два потока, три вопроса, машинный порог, регистр исключений с датами. Если текст длиннее этого, в нём, скорее всего, обещания вместо механизмов.

Мы неправы в двух случаях. Первый: маленький граф зависимостей без транзитивной паутины, редкие релизы, продукт в режиме поддержки. Тогда настройка потока дороже ручных обновлений по списку, и разумнее список с календарным напоминанием, а не бот. Второй: продукт заморожен по решению. Тогда политики нет вовсе — есть абзац «не обновляем, и вот почему».

Если неясно, к какому случаю вы ближе, — начните с измерения времени одного обновления. Это дешевле, чем полгода жизни под политикой, которую никто не соблюдает.

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

Среди нескольких сборок поиском выделена одна — та, что содержит искомую библиотеку: так перечень отвечает на запрос вместо мёртвого файлавсе сборкиесть библиотека
Усиление

Цепочка поставок: перечень, который кто-то читает

В цепочке поставок та же история: перечень состава генерируют, кладут рядом — и не читает никто. Разобраны шаги, которые превращают файл в индекс, отвечающий на запросы по всем сборкам.

Цепочка шагов конвейера CI: чужой код входит, исполняется с ключами, и последний шаг — публикация — штатно выводит секрет за границу доверияграница довериясекрет наружу
Углубление

Секреты в CI: пять типовых утечек

Даже хранилище секретов и маскирование логов не спасают от утечки — разбор пяти мест, где конвейер CI теряет ключи.

Две угрозы по разные стороны: одна закрывается хранилищем, другая — серверомчужой скриптчужой сайт
Углубление

Токен в браузере: где его хранить

Глубже про хранение: две угрозы вместо одной, что даёт каждое место и почему «спрятать получше» ответом не является.

Сетка ячеек — список активов компании, заполнена лишь часть; рядом счётчик показывает большее число: столько всего о компании видно снаружи, чего нет в списке.видно снаружи
Практика

Что о вашей компании видно снаружи

Как обнаружить забытые активы и уязвимости компании через открытые данные — пошаговая проверка без платных инструментов.

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

Начните вводить — материалы появятся здесь.

выбрать · Enter открыть · Esc закрыть