惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Blog of Author Tim Ferriss
Microsoft Azure Blog
Microsoft Azure Blog
S
SegmentFault 最新的问题
Schneier on Security
Schneier on Security
W
WeLiveSecurity
Webroot Blog
Webroot Blog
T
Threatpost
量子位
大猫的无限游戏
大猫的无限游戏
C
Cisco Blogs
腾讯CDC
N
News | PayPal Newsroom
T
Troy Hunt's Blog
T
Tailwind CSS Blog
Latest news
Latest news
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Register - Security
The Register - Security
Know Your Adversary
Know Your Adversary
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Engineering at Meta
Engineering at Meta
SecWiki News
SecWiki News
MyScale Blog
MyScale Blog
GbyAI
GbyAI
Application and Cybersecurity Blog
Application and Cybersecurity Blog
A
Arctic Wolf
The GitHub Blog
The GitHub Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Help Net Security
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
有赞技术团队
有赞技术团队
NISL@THU
NISL@THU
L
LINUX DO - 最新话题
雷峰网
雷峰网
P
Privacy International News Feed
Spread Privacy
Spread Privacy
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
月光博客
月光博客
V
V2EX
H
Help Net Security
博客园 - 三生石上(FineUI控件)
U
Unit 42
B
Blog
PCI Perspectives
PCI Perspectives
G
GRAHAM CLULEY
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
Last Week in AI
Last Week in AI

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Impact Analysis в дизайн-системе: как мы сделали CI осмысленнее, а review понятнее
Даниил · 2026-05-27 · via Все публикации подряд на Хабре

Меня зовут Даниил, я Android-разработчик в «БКС Мир инвестиций».

В первой статье мой коллега рассказывал, как мы использовали Kotlin IR Compiler Plugin, чтобы автоматически добавлять testTag и semantics в Compose-компоненты: Kotlin IR Compiler Plugin в дизайн-системе: автотесты с Compose без ручной разметки.

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

Если коротко, то кейс выглядит вот так:

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

  • Решение: мы строим карту влияния — структурированное описание компонентов и связей «кто кого использует», на уровне самой дизайн-системы. Compiler plugin сохраняет manifest этих связей, а impact analysis превращает изменение в описанную зону риска.

  • Результат: CI в первую очередь запускает проверки, привязанные к затронутым компонентам и к цепочке их использования от источника изменения, а отчёт в merge request показывает автору, ревьюеру, тестировщику и дизайнеру, что задели напрямую, что может быть затронуто по цепочке, какие проверки уже есть и где покрытие стоит усилить.

Для нас это оказалось важнее, чем просто “запускать меньше тестов”. Impact analysis стал общим языком между CI, разработчиком, ревьюером, тестировщиком и дизайнером. CI получает список необходимых проверок, а люди получают контекст риска: какие компоненты и сценарии стоит проверить дополнительно, даже если они не менялись напрямую.

Дальше расскажу, как мы переложили impact analysis на язык компонентной библиотеки: построили карту зависимостей, связали её с превью и тестами и превратили результат в понятный отчёт для merge request.

Почему полный прогон перестал быть хорошим ответом

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

Но дизайн-система постепенно растёт:

  • компонентов становится больше;

  • появляются разные уровни: core, base, составные компоненты;

  • один компонент начинает использовать другие;

  • у компонентов появляются состояния, варианты отображения, превью;

  • растёт число unit, screenshot, автотестов;

  • разные продуктовые команды зависят от одной общей библиотеки.

В такой системе маленькая правка не всегда остаётся маленькой.

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

У нас одновременно появилось несколько проблем:

  • поиск зависимых компонентов: нужно понимать, куда может дойти изменение;

  • скорость CI: полный прогон всех проверок становится дорогим;

  • хрупкость ручного выбора тестов: легко забыть проверить компонент, который использует измененный компонент;

  • неочевидные зоны, где покрытие стоит усилить: иногда изменение задевает компонент, у которого нет превью или screenshot-теста;

  • сложность review и ручной проверки: ревьюеру, тестировщику и дизайнеру нужно понять не только “что поменялось”, но и “что это может задеть”.

Так появилась задача: построить механизм, который показывает область влияния изменения, помогает CI выбрать набор проверок по графу зависимостей от источника изменения и даёт участникам MR понятный список компонентов и сценариев, на которые имеет смысл обратить внимание при приёмочной проверке или при ручном прогоне в демо.

Что обычно делают в таких случаях

Проблема выборочного запуска проверок не новая. Когда тестов становится много, команды обычно приходят к одному и тому же вопросу: как не запускать всё каждый раз, но всё ещё доверять CI?

Мы смотрели на несколько подходов.

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

Второй подход — smoke/sanity-наборы. Они хорошо отвечают на вопрос “система вообще жива?”. Но нам нужен был другой ответ: “что именно могло сломаться из-за этого изменения?”. Поэтому smoke-наборы полезны как быстрый сигнал, но не решают задачу анализа влияния.

Третийподход — impact analysis на уровне проекта или модулей. Такой подход хорошо описан в Android-сообществе. Например, Циан писал, как ускорять проверки через impact-анализ для статических анализаторов и unit-тестов. Ситимобил показывал impact-анализ на примере Android-проекта.

Из этих материалов для нас были важны две мысли:

  • анализ должен идти не только от изменённого места, но и по зависимостям;

  • результат анализа должен быть пригоден и для CI, и для людей, которые принимают решение в MR.

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

Поэтому мы взяли общую идею impact analysis, но применили её к компонентам дизайн-системы: их связям, состояниям, превью и тестовому покрытию. Границы Gradle-модулей и экранов приложения здесь вторичны; важнее вопрос «какой компонент затронут, кто его использует, какие превью и тесты относятся к этой части графа?» — именно так мы переложили impact analysis на язык компонентной библиотеки.

Представить дизайн-систему как карту влияния

Дальше под картой мы будем иметь в виду описание компонентов и рёбер «использует / используется в» между ними; позже к этой базе добавятся state, превью и привязки тестов.

Мы начали с простого вопроса:

Если изменился компонент, как понять, какие компоненты могут быть затронуты этим изменением?

Ответ оказался в структуре самой дизайн-системы.

Компоненты связаны между собой:

  • один компонент может использовать другой;

  • базовый компонент может входить в несколько составных;

  • состояние одного уровня может влиять на поведение другого;

  • тест может проверять не один компонент, а связку из нескольких.

Например, изменение в ButtonCore может быть важно для ButtonBase, а через него - для ButtonBarBase.

Примерная иерархия компонента

Примерная иерархия компонента

Картинка 1. Цепочка ButtonCoreButtonBaseButtonBarBase: изменение в нижнем слое может подняться выше по использованию (выше по схеме — «…», иерархия продолжается).

Так обычная иерархия UI-компонентов превращается в граф влияния: мы видим не только место изменения, но и путь, по которому оно может подняться выше.

Откуда берётся карта

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

У нас уже был более точный источник данных - compiler plugin из первой статьи. Если совсем кратко, plugin проходит по IR-дереву, находит компоненты с @CodegenModifier и работает не с текстом файла, а со структурой Kotlin-кода. Подробно этот механизм разобран отдельно: Kotlin IR Compiler Plugin в дизайн-системе: автотесты с Compose без ручной разметки.

Для impact analysis мы используем тот же этап компиляции, но сохраняем не test semantics, а карту компонентов в manifest. По сути, manifest - это JSON-слепок структуры дизайн-системы: имя компонента, путь к исходнику, слой, прямые зависимости и связанные state-модели.

Например, одна запись в manifest может выглядеть так:

{
  "componentName": "ButtonCore",
  "sourcePath": "compose_uikit/.../button/ButtonCore.kt",
  "layer": "core",
  "directComponentDependencies": [
    "Typography"
  ],
  "stateReferences": [
    {
      "typeName": "ButtonCoreState",
      "sourcePath": "compose_uikit/.../button/ButtonCoreState.kt"
    }
  ]
}

Сам процесс генерации устроен так:

  1. Kotlin-компилятор строит IR-дерево проекта.

  2. Наш compiler plugin проходит по этому IR-дереву.

Почему опираемся на IR, а не переключаемся, скажем, на FIR или разбор исходников как AST? На другой стадии компилятора тоже можно было бы извлечь зависимости, но у нас уже есть рабочий IR-проход из первой статьи: один pipeline, одна точка сопровождения, меньше дублирования. IR в нашем случае — зрелое представление программы, по которому стабильно обходить вызовы и структуру кода; второй плагин «рядом» оказывается дешевле, чем параллельная реализация на другом уровне ради того же результата.

  1. Когда plugin находит функцию с @CodegenModifier, он собирает данные о компоненте: имя, путь к исходнику, слой, прямые зависимости и связанные state-модели.

  2. Эти данные складываются во внутренний список записей manifest.

  3. После обхода модуля plugin записывает список в JSON-файл.

То есть manifest не поддерживается руками. Он появляется автоматически во время компиляции и становится готовой картой компонентов для дальнейшего анализа.

Общая схема impact analysis

Общая схема impact analysis

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

Как определяется зона риска

Зона риска - это ответ на вопрос: “куда стоит посмотреть после изменения?”.

Допустим, изменился ButtonCore. Это не значит, что проблема может быть только внутри ButtonCore. Он может быть маленькой деталью, на которой держатся более крупные компоненты.

Анализ начинает с источника изменения и постепенно расширяет область:

  1. Сначала в зоне риска сам ButtonCore.

  2. Потом компоненты, которые используют его напрямую, например ButtonBase.

  3. Затем компоненты выше по цепочке, например ButtonBarBase.

В отчёте такая цепочка может выглядеть так:

ButtonCore -> ButtonBase -> ButtonBarBase

Важно: это не “порядок выполнения кода”, а маршрут распространения риска. Если мы меняем нижний слой (ButtonCore), проблема может проявиться не сразу в нём, а выше — там, где этот слой используется как часть более сложного компонента. Поэтому в review полезно смотреть не только дифф исходного файла, но и всю цепочку использования.

Если изменение касается общей модели состояния, зона риска может расшириться сразу в несколько веток. Это не ошибка анализа, а полезный сигнал: общий state действительно может влиять на несколько компонентов одновременно.

Дальше к этой зоне добавляются практические подсказки:

  • в каком демо быстрее всего проверить сценарий вручную;

  • какие тесты уже покрывают эту область;

  • где покрытие стоит усилить (например, у затронутого компонента нет screenshot-теста или собственного превью);

  • какие проверки имеет смысл запустить первыми.

Как работает impact analysis

Как работает impact analysis

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

Поэтому итоговый отчёт — это не просто список затронутых компонентов. Это подсказка для всех, кто смотрит MR: откуда началось изменение, куда оно может дойти, что уже покрыто тестами, где стоит быть внимательнее и какие сценарии разумнее дополнительно прогнать вручную (в демо или при приёмочной проверке).

Как тесты связываются с компонентами

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

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

Граф зависимостей между компонентами отвечает на вопрос: куда может распространиться изменение. Compiler plugin при компиляции строит manifest: какой composable является компонентом, какие state-модели он использует и какие другие компоненты вызывает.

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

Например, unit-тест формата состояния кнопки привязывается к ButtonBase:

@TestedComponent("ButtonBase")
class ButtonBaseTest {
    // unit tests
}

Если потом меняется ButtonCore, impact analysis проходит по графу вверх, видит, что изменение влияет на ButtonBase, и выбирает тесты, привязанные к ButtonBase.

изменение: ButtonCore
граф:      ButtonCore -> ButtonBase
тесты:     ButtonBaseTest, ButtonBasePaparazziTest

В этом и есть важная особенность схемы: тест описывает свой проверяемый контракт, а impact analysis сам сопоставляет этот контракт с зоной риска.

Один тест может иметь и несколько точек покрытия. Это полезно для сценариев, которые проверяют общий контракт нескольких самостоятельных компонентов. Например, unit-тест может проверять общий formatter, используемый в InputValueBase и InputDateBase, или screenshot-сценарий может сравнивать несколько публичных вариантов компонента в одном тестовом классе:

@TestedComponent("InputValueBase", "InputDateBase")
class InputFormattingTest {
    // tests for shared visible formatting contract
}

Если завтра InputDateBase начнёт использовать новый внутренний InputCalendarCore, граф увидит новую связь сам, а тест останется привязан к публичному компоненту, поведение которого он проверяет. Так покрытие остаётся привязанным к продуктовой сути сценария, а не к текущей внутренней реализации.

Такой контракт хорошо читается на review. Если тест покрывает компонент, это видно прямо в коде теста. Если компонент попал в зону риска, CI может найти связанные проверки и запустить их точечно.

Что видит команда в MR

Главный результат для команды - понятный отчёт в merge request.

Упрощённо отчёт может выглядеть так:

Источник изменения:
- ButtonCoreState

Зона риска:
- ButtonCore
- ButtonBase
- ButtonBarBase

Цепочки влияния:
- ButtonCore -> ButtonBase
- ButtonCore -> ButtonBase -> ButtonBarBase

Найденные проверки:
- ButtonCoreStateTest
- ButtonBaseTest
- ButtonBasePaparazziTest

Зоны, где покрытие стоит усилить:
- ButtonBarBase без собственного превью
- ButtonBarBase без screenshot-теста

Такой отчёт полезен ещё до запуска CI. Автор MR видит, какие проверки стоит запустить адресно. Ревьюер быстрее понимает, куда смотреть глубже. Тестировщик получает список сценариев для ручной проверки, а дизайнер может заметить, какие соседние компоненты или состояния стоит сверить визуально.

Что меняется в CI

В CI impact analysis работает как фильтр перед запуском проверок.

Если зона риска понятна, CI начинает с тестов, которые связаны именно с ней. Например, если изменение вокруг кнопки, сначала запускаются проверки кнопки и компонентов, которые её используют.

Работа CI

Работа CI

Картинка 4. CI в первую очередь запускает проверки, привязанные к затронутым компонентам и к цепочке их использования, но сохраняет полный прогон как безопасный fallback.

В типовом случае это сокращает объём проверок и ускоряет обратную связь в merge request.

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

Практический эффект для команды

Чтобы не оставаться на уровне “стало удобнее”, мы смотрели на три прикладных эффекта: время до первого полезного сигнала в MR, долю полных прогонов и понятность review.

  • Скорость обратной связи. В типичных MR с локальными правками first feedback приходит быстрее, потому что CI начинает с узкого набора проверок по графу затронутых компонентов, а не с полного suite.

  • Изменение workflow. Автор MR сначала читает impact-отчёт, проверяет риск-цепочки и запускает релевантные тесты адресно. Ревьюер, тестировщик и дизайнер получают ту же карту и быстрее выбирают, куда смотреть глубже.

  • Окупаемость. Подход особенно окупается в потоках с частыми изменениями базовых компонентов: меньше “лишних” прогонов, меньше ручного гадания по зависимостям, меньше возвратов в MR после поздно найденных эффектов.

Мы сознательно оставили полный регресс как страховку. Поэтому выигрыш здесь не в “максимальном урезании тестов”, а в том, что большая часть обычных MR обрабатывается быстрее и предсказуемее без потери безопасного сценария.

Что изменилось в процессе

Главное изменение оказалось не только в скорости CI. До impact analysis проверка выглядела как чёрный ящик: разработчик отправил MR, CI что-то запустил, в конце сталопонятно — зелёное или красное.

С картой влияния между изменением и результатом появляется контекст. Видно, где началась правка, какие компоненты она может задеть, какие проверки связаны с этой зоной и где покрытие стоит усилить.

Это полезно и для автора MR, и для ревьюера, и для тех, кто проверяет поведение или визуальное состояние компонента руками. Изменение перестаёт быть локальной догадкой и становится описанной областью риска для всех участников процесса.

Где подход может подвести

Impact analysis настолько хорош, насколько хороши данные, на которых он построен.

Есть два неприятных сценария.

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

Второй - пропуск связи. Он опаснее. Такое возможно, если компонент не попал в карту, зависимость не видна анализу или тест не привязан к компоненту. Тогда система просто не узнает, что между изменением и проверкой есть связь.

Поэтому мы не считаем результат анализа абсолютной истиной. Это подсказка, а не оракул.

Качество данных приходится поддерживать отдельно:

  • компоненты должны попадать в карту через общий механизм разметки;

  • тесты должны явно говорить, какие компоненты они покрывают;

  • полный прогон должен оставаться fallback для сложных или сомнительных случаев.

Отдельный компромисс — annotation-based mapping тестов. Да, тест нужно явно связать с компонентом. Это дополнительная дисциплина. Но зато связь видна в коде, ревьюится вместе с тестом и не живёт в отдельной ручной таблице, которую легко забыть обновить.

В итоге impact analysis помогает сфокусироваться, но не заменяет review и инженерное мышление.

Что можно развивать дальше

Дальше хочется сделать анализ точнее: учитывать не только факт изменения state-модели, но и конкретные поля, которые использует компонент.

Ещё одно направление — связать impact analysis с тестовым контрактом из первой статьи. Если компонент уже описывает себя через стабильные test semantics, на этой базе можно строить Page Object или подсказки для тестировщиков.

Краткий итог

Наша исходная гипотеза была простой: если построить карту влияния изменения и связать её с покрытием, то CI станет быстрее и полезнее для принятия решений, а не только для “красный/зелёный”.

На практике это подтвердилось: impact analysis стал для нас способом сделать изменения в дизайн-системе ожидаемыми.

Не просто “поменяли файл - запустили тесты”, а:

изменение
  -> источник риска
  -> затронутые компоненты
  -> связанные проверки
  -> понятный feedback в MR

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

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

На примере ButtonCore это уже видно в минутах: в локальном замере targeted-прогон unit и Paparazzi занял 42 секунды вместо 53 секунд полного прогона. При этом ButtonCore — низкий компонент в иерархии, от которого расходится широкая зона влияния. Для более локальных изменений выигрыш будет заметнее, а по мере роста количества тестов вокруг компонентов разрыв между полным suite и targeted-прогоном будет только увеличиваться.

Когда такая модель появляется, CI перестаёт быть чёрным ящиком с кнопкой “запустить всё”. Он становится частью инженерной обратной связи: показывает impact, подсвечивает риски и помогает быстрее принимать решения в merge request.