bythe.net
← К ленте
Тренд

CI не успевает за агентами: как Linear пересобрал свой конвейер

Тестовый набор вырос вчетверо за полгода, а ожидание пулл-реквеста стало короче: разбор серии мелких оптимизаций, которые вернули Linear контроль над сборками.

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

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

Сначала — железо и компиляторы

Первый шаг оказался самым банальным: уход с раннеров GitHub Actions на сторонние машины с более быстрыми процессорами и дисками. Среднее ускорение — 34 процента, а на компиляции TypeScript — 52. Никакой инженерной хитрости здесь нет, это чистая покупка производительности, но она мгновенно окупается, когда счёт идёт на сотни тысяч минут в месяц.

Вторым срезом стала замена тулинга. Переход на нативный компилятор tsgo уменьшил время проверки типов на 73 процента, линтинг переехал на Oxlint. Это тот редкий случай, когда переписывание инструментов на системных языках даёт эффект, видимый не в бенчмарке, а в календаре разработчика.

Потом — всё, что происходит до первого теста

Самая недооценённая часть конвейера — подготовка. Джоба, которая определяет, какие проверки вообще нужно запускать для этого пулл-реквеста, занимала 26 секунд по медиане; её ужали до восьми. Клонирование репозитория перевели на sparse- и blobless-чекауты с обрезанной историей, а сам чекаут обернули в логику ретраев с таймаутами — потому что на масштабе сетевые сбои перестают быть исключением и становятся статистикой.

Зависимости предустановили прямо в базовый образ CI, а установку пакетов ограничили только теми частями монорепозитория, которые реально нужны конкретной джобе: 44–73 секунды превратились в 16–18. От кеширования node_modules отказались вовсе — пересобрать оказалось быстрее, чем достать из кеша. Запись кешей перенесли в джобы, которые идут после тестов, чтобы не задерживать то, чего все ждут. Схему базы данных перестали получать проигрыванием миграций и начали разворачивать из снапшота: 12 секунд превратились в одну-две.

Итог по подготовке: 110–140 секунд на шард стали 67–73. Сорок четыре процента — там, где обычно никто не ищет, потому что «это же просто setup».

И только потом — сами тесты

Семь отдельных проверок свели в две пакетные джобы. Одна эта перестановка сэкономила около 87 тысяч минут раннеров в месяц — почти 12 процентов всего расхода на CI.

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

Самым крупным одиночным выигрышем — 17 процентов месячного расхода — оказалась возможность делить состояние модулей между тестами внутри одного воркера. Это ровно та оптимизация, которую в приличном обществе не рекомендуют: общее состояние между тестами — классический источник флаки-прогонов и дебага по ночам. Linear оставили строгую изоляцию правилом по умолчанию, а совместное состояние сделали опциональным, включаемым явным комментарием в файле. То есть цена за скорость взимается не со всего кода сразу, а с тех мест, где автор теста готов её заплатить осознанно.

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

Что из этого следует

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

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

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

Подробный разбор с графиками — в блоге Linear.

Войти, чтобы оценить материал

Комментарии

Войти, чтобы оставить комментарий