Всем привет! Меня зовут Алексей, я техлид Android-направления в компании Домклик.
В прошлой статье мы рассмотрели несколько базовых метрик. В этой части поговорим о менее очевидных, но не менее полезных критериях и способах измерения различных аспектов мобильной разработки. Тема не новая, однако получила новый импульс благодаря развитию и широкому внедрению искусственного интеллекта (ИИ).
Lead Time
В предыдущей статье мы частично затронули метрику «длительность сборки», которая легко конвертируется в деньги. Однако с точки зрения бизнеса гораздо важнее отслеживать время, затрачиваемое на создание ценности и её доставку пользователям — то есть Time To Market (TTM). Поскольку подготовка и разработка фичи обычно включают множество процессов, часто не связанных между собой, точное измерение TTM может быть сложной задачей, особенно в крупных компаниях.
Чтобы упростить задачу, можно представить разработку фичи как последовательность отдельных этапов (задач), каждый из которых можно измерить отдельно (Lead Time). Имея временные метки для каждого этапа, мы сможем анализировать длительность каждого интервала во всей системе.
Рассмотрим примерный набор процессов создания фичи в мобильном приложении:
Бизнес создаёт и описывает задачу в трекере — это артефакт идеи фичи и начальная точка TTM. Однако физически задачу нельзя описать одномоментно: её уточняют, собирают дополнительные данные, проводят исследования, обсуждения, согласования и т.д. Время от заведения задачи до передачи её в разработку может варьироваться от одного дня до года, что не очень удобно, если мы хотим извлечь какую-то пользу. Поэтому уже на этом этапе стоит пересмотреть шаги TTM.
После формулировки и описания задача передаётся в разработку. Обычно это подразумевает заведение новой задачи. Важно уметь отличать её от других (тип, метки или другие уникальные свойства), а также фиксировать историю и время изменений (создание, взятие в работу, закрытие и т.п.). Здесь я намеренно опустил этапы подготовки дизайна и архитектуры, поскольку с точки зрения процессов они мало отличаются от разработки.
Задача по разработке считается завершённой только после прохождения этапа «приёмки», который обычно включает проверку кода, тестирование и сборку (успешное прохождение всех автотестов и других Quality Gates). Проще всего контролировать эти этапы через специальные статусы в трекере задач, особенно если их перевод происходит автоматически.
Далее, в зависимости от того, когда вы считаете задачу закрытой — например, сразу после вливания кода в Develop/Master/Release — наступает этап доставки до пользователя. В этот период входит ожидание начала релизного цикла, подготовка релиза (создание ветки, тестирование, частичная раскатка) и публикация приложения в магазине. Для простоты предположим, что после публикации пользователи получают фичу мгновенно. На этом этапе задаче нужно присвоить ещё одно свойство — принадлежность к конкретному релизу. Если раскатка релиза завершена, задача с номером версии этого релиза считается полностью выполненной.

Итак, сформирован приблизительный цикл общего Lead Time для определённой бизнес-задачи. Рассмотрим его этапы, начиная со старта разработки, более подробно. Поскольку общий Lead Time процесса разработки состоит из нескольких этапов, удобнее анализировать его как совокупность метрик Lead Time по каждому из них. Такой подход позволит в будущем точнее выявлять, на каких этапах уходит больше всего времени (или, наоборот, почти не тратится — такое тоже бывает), и сосредоточить внимание именно на этих участках.
Я предлагаю рассматривать общий Lead Time как сумму длительности разработки и длительности релизного цикла, где длительность разработки включает в себя работу над задачей, прохождение проверок, тестирование и влитие.
Review Lead Time
Как мы уже выяснили, разработку нельзя считать завершённой, пока не пройдена проверка (Review). Чем крупнее компания, тем больше внимания и времени уделяется этому процессу. При этом существует несколько потенциальных точек «простоя»:
Ожидание, пока необходимое количество владельцев кода, проверяющих или ИИ просмотрят код, а затем — исправление замечаний
Ожидание успешной сборки, прохождения тестов и гейтов, исправление сломанных тестов, пересборка и очередь в CI/CD
Каждый из этих этапов заслуживает особого внимания, но в рамках TTM нас интересует общая длительность прохождения Review — то есть интервал между созданием запроса на влитие (Pull/Merge Request) и его влитием. Поскольку большинство процессов привязано к задаче, логично фиксировать начало и завершение проверок через статусы задачи. Большинство систем контроля версий поддерживают хуки, которые можно настроить на соответствующие события, вызывая обновление статуса задачи через API трекера.
Чаще всего тестирование задачи или проверка дизайна происходит либо в статусе «В работе», либо параллельно с проверкой кода. Иными словами, время, затраченное на тестирование, уже учтено в рамках других этапов. Поэтому строить графики Lead Time имеет смысл только при чётком понимании временных промежутков. На данный момент у нас не ведётся отдельный учёт ручного тестирования, так как отсутствует возможность автоматизировать фиксацию начала и завершения этого процесса (в том числе с помощью ИИ), а также нет возможности повлиять потом на эту метрику.
Зато мы можем влиять на длительность прохождения Review. Когда мы только начали отслеживать этот показатель, медианная длительность проверок составляла около трёх дней. После оптимизации сборки, распараллеливания шагов конвейера, Impact-анализа тестов, управления очередями в CI/CD и небольших автоматизаций вроде автовливания или автоматических пересборок, мы наблюдаем планомерное снижение медианы — теперь она составляет менее суток.
Develop Lead Time
Здесь всё просто: через API получаем из истории задачи в трекере время её взятия в работу и время закрытия. Под закрытием понимается самый последний статус в Workflow, при этом задача обязательно проходит через этапы прогресса, код-ревью, тестирования и т.д. Задачи фильтруются по командам, платформам, разработчикам и другим параметрам.
Ранее не было смысла отдельно отслеживать длительность разработки, поскольку отсутствовала возможность на неё влиять. Однако с интеграцией ИИ в процесс разработки и развитием концепции AI SDLC эта метрика стала одной из самых востребованных. Помимо понимания бизнесом затрат в человеко-часах на разработку фичи, возникла необходимость оценивать эффективность инвестиций в развитие и приобретение ИИ-инфраструктуры.
Сбор метрик в контексте ИИ мы рассмотрим в следующих статьях, а здесь сосредоточимся на расчёте нашего TTM.
Release Lead Time
Самое интересное начинается, когда возникает необходимость отследить время, за которое готовая фича попадает на устройство пользователя. Релизный цикл мобильных приложений — процесс сложный, длительный и сильно варьируется в разных компаниях и на разных платформах. Он может занимать недели или даже месяц и зависит не только от внутренних процедур, таких как проверки безопасности и регресс-тестирование, но и от внешних факторов — например, проверки и модерации в магазинах приложений. Кроме того, санкционная политика некоторых магазинов добавляет свои нюансы.
Типичные этапы релиза выглядят так:
Плановый отвод ветки раз с определённой периодичностью
Сборка приложения, прохождение тестов, регрессов, пентестов и других проверок
Отправка на проверку в магазины приложений
Поэтапная раскатка
Хотфиксы при обнаружении проблем во время раскатки
Полная раскатка и завершение релизного цикла
Первые три шага регулируются внутренними процессами компании. Практика показывает, что критическое влияние на TTM оказывает необходимость полного регресса, который для крупного приложения может занимать несколько рабочих дней. Поскольку автоматизация тестирования — дорогостоящая инвестиция, важно иметь измеримые метрики, которые служат аргументами для её обоснования.
Задержки на последних трёх этапах носят стихийный или неконтролируемый характер: невозможно повлиять на длительность модерации (например, в Samsung Galaxy Store может занимать больше недели) или на случайные сбои, возникающие, например, в WebView.
Тем не менее, независимо от длительности этих этапов, они должны укладываться в установленный компанией период, чтобы избежать накопления релизов и их отсрочек. Метрики Lead Time позволяют держать процесс на контроле. Для этого мы разработали специальный сервис, в котором ведём всю работу над релизами. В нём фиксируем ключевые вехи — например, отвод ветки и факт начала полной раскатки в одном или во всех магазинах.
Добавив хук, проставляющий в задачу версию приложения при отводе ветки, мы можем построить общий Lead Time. Это агрегированный интервал между началом работы над задачей и стартом раскатки для всех закрытых задач, сгруппированных по версии приложения.
Результат достигнут, но важно помнить, что часть пути задачи до попадания в Production находится вне нашего контроля. С точки зрения мониторинга удобнее разделять борды на те части процесса, на которые можно влиять.
Test Coverage
Модульные тесты
Покрытие Unit-тестами — давно известная метрика, однако с появлением ИИ появилась отличная возможность без значительных усилий достигать почти 100%-го покрытия. Теперь эта метрика служит не только для контроля качества работы кода, но и как инструмент проверки корректности генерации кода ИИ.
Для Android по-прежнему широко используется библиотека JaCoCo. Чтобы настроить её, подключаем плагин и регистрируем собственную Gradle-задачу от JacocoReport. Затем указываем необходимые настройки, расположение файлов с исходниками и генерируемый плагином .exec файл, не забывая установить dependsOn от задач запуска тестов. Рекомендую также задать исключения (Excludes), поскольку очевидно, что не все файлы в проекте покрываются тестами. По мере роста проекта, объёма генерируемого кода и количества используемых фреймворков поддерживать исключения становится всё сложнее. В таком случае можно рассмотреть альтернативный подход — перечислять регулярки классов, которые обязательно должны иметь тесты, при условии, что их наименования или пакеты стандартизированы в проекте.
Примерно так может выглядеть настройка JaCoCo
tasks.register("${testTaskName}Coverage", JacocoReport.class) { dependsOn("$testTaskName") group = "Reporting" description = "Generate Jacoco coverage reports for the ${buildTypeName.capitalize()} build." reports.html { enabled = true } reports.xml { enabled = true required = true } def includes = [ '**/*ViewModel.class', '**/*UseCase.class', '...' ] def compileKotlinTask = tasks.named("compile${variantName.capitalize()}Kotlin") classDirectories.from = compileKotlinTask.map { task -> files(task.outputs.files.collect { dir -> fileTree(dir: dir, includes: includes) }) } def sourceDirs = [ "$project.projectDir/src/*/java", "$project.projectDir/src/*/kotlin", ] sourceDirectories.from = files(sourceDirs) executionData.from = files("${project.layout.buildDirectory.asFile.get()}/jacoco/${testTaskName}.exec") }
После успешной настройки при прогоне новой Gradle-задачи формируется отчёт в build/reports/jacoco/. В этом отчёте для каждого класса рассчитывается покрытие по линиям, веткам или инструкциям. В нашей практике мы используем его исключительно для извлечения абсолютных или агрегированных значений покрытия по командам разработки и модулям, чтобы затем отправить их в Prometheus. Кроме того, отчёт может использовать ИИ-агент для проверки качества написанных тестов.
UI тесты
В Android-разработке JaCoCo традиционно ассоциируется с модульными тестами, однако ничто не мешает применять его и для тестов интерфейса. Для этого потребуется дополнительная настройка и, возможно, переосмысление подхода.
Исторически у нас все UI-тесты сосредоточены в одном модуле, что создаёт определённые сложности при расчёте покрытия. Например, отсутствует однозначный способ связать конкретный тест с Gradle-модулем, который он покрывает, не говоря уже о конкретной фиче или экране, если в модуле их несколько. В результате определение покрытия при прогоне одного или группы тестов становится достаточно затратным. Поэтому мы выбрали другой путь: расчёт покрытия активируется только при плановом прогоне регресса — то есть при запуске всех UI-тестов проекта ночью на ветках Develop или релизных. Итоговый результат затем разбивается по командам или модулям на основе группировки по имени пакета в классах. Такой подход не позволяет определить качество конкретного теста, но даёт понимание о качестве покрытия каждой фичи.
Первым шагом мы активируем свойство testCoverageEnabled в нужном buildType в build.gradle модуля с тестами (в нашем случае). Поскольку покрытие собирается только в определённых сборках, значение для testCoverageEnabled берётся из project.property. Далее необходимо поддержать кастомные раннеры, если они используются — например, для Marathon нужно активировать свойство codeCoverageEnabled.
После этого создаём собственную Gradle-задачу по аналогии с модульными тестами, но с одной особенностью: исходники и exec-файлы собираются со всех subprojects основного проекта. При запуске Marathon на всём проекте (например, для регресса) задача собирает все exec-файлы в одной папке:
"${buildDir}/reports/marathon/${flavor}${buildType.capitalize()}AndroidTest/device-files/omni/coverage"
На этот раз мы сознательно не добавляли никаких исключений для классов или пакетов, исходя из предположения, что хороший тест интерфейса должен, по возможности, охватывать все части фичи, то есть использовать все инструкции кода.
В результате мы получаем целостную картину покрытия интерфейса тестами.

Анализ кода
По мере роста проекта увеличивается и сложность контроля за Legacy, техническим долгом, миграциями на новые фреймворки и технологии, адаптацией ЕДС и другими аспектами. Для управления всеми этими процессами можно настроить сбор статистики с помощью собственного Gradle‑плагина. Такой плагин выступит в роли статического анализатора проекта, сохраняющего результаты расчёта в общей системе мониторинга.
Принцип работы плагина прост: вы регистрируете его в корневом build.gradle , и на фазе конфигурации получаете доступ к файлам и папкам всех модулей. Данные можно собирать напрямую или предварительно формировать таблицу с индексами (возможно, у вас уже есть подобные инструменты, если вы используете ИИ в крупных проектах). Затем собранную информацию передают в соответствующую кастомную Gradle-задачу, которая формирует итоговый отчёт. В нашем случае есть одна задача, зависящая от всех задач по сбору метрик, именно её вызывают в CI/CD для отправки данных по проекту.
Всё достаточно просто. Из нюансов стоит отметить необходимость корректной фильтрации модулей и файлов. Например, если вы хотите отслеживать внедрение дизайн-системы, то не стоит учитывать модули типаcom.android.library и не относящиеся к UI-слою классы и пакеты.
Заключение
В этой части мы разобрали метрики, которые не всегда очевидны: Lead Time и его составляющие, покрытие Unit- и UI-тестами, а также статический анализ техдолга и Legacy. Вместе с базовыми метриками из первой части они дают целостное представление о том, где именно «съедается» время, что можно улучшить и где оправданы автоматизация и внедрение ИИ. В следующих статьях мы поговорим о сборе и интерпретации метрик разработки в контексте AI SDLC, а также вернёмся к метрикам работы приложения на устройствах пользователей.