Coding agent почти починил мне CameraX задержкой. Почему я его остановил
В Android-приложении над которым я работаю есть сканер штрихкодов на CameraX. На планшете обнаружился странный баг: если перевернуть устройство на 180°, картинка камеры на экране остаётся правильной, а кадры, которые получает анализатор, оказываются перевёрнутыми.
То есть одна камера в один момент времени как будто имеет две ориентации:
Сам баг оказался не особенно большим. Интереснее было то, как мы его искали.
Последнее время я практически всю разработку веду через coding agents. В этой задаче агент сам работал с проектом, менял код, запускал adb, писал logcat, анализировал результаты и обновлял MR.
У меня оставалась одна операция, которую он пока не мог выполнить: физически переворачивать планшет.
Получилось довольно необычное парное программирование.
«Запускай запись, я переверну экран»
Один из циклов отладки выглядел буквально так:
Агент запускал adb logcat на подключённом планшете, получал данные, менял instrumentation и снова просил меня повторить эксперимент.
Это уже довольно далеко от сценария «скопировал ошибку в ChatGPT и попросил написать код».
Фактически мы строили обычный инженерный цикл:
Только большую часть механических операций выполнял агент.
И тут появилось «давайте немного подождём»
На одном из этапов агент обнаружил интересную вещь.
После физического переворота callback изменения display уже приходил, но previewView.display.rotation в этот момент всё ещё содержал старое значение.
Примерно через полсекунды значение становилось правильным.
Вывод агента был таким:
И вот здесь я остановил работу.
Я ещё не знал правильного решения. Не знал, нужен ли другой listener, надо ли менять настройки ImageAnalysis или проблема вообще находится глубже в обработке YUV.
Но «значение обновляется чуть позже, поэтому давайте его подождём, а потом перебиндим камеру» для меня выглядело как red flag.
Сегодня это полсекунды. На другом планшете --- сколько? А что именно мы ждём? Почему вообще для поворота камеры нужно пересоздавать use cases?
Я попросил ничего больше не исправлять и вместо этого доказать причину.
Не «почини», а «покажи, где расходится состояние»
Мы добавили в диагностику несколько значений:
И повторили эксперимент.
До переворота получили:
После переворота:
Вот здесь баг практически перестал быть загадкой.
Планшет физически перешёл:
Но для Android оба положения остаются landscape:
Поэтому Configuration.orientation как был 2, так и остался.
Сам display уже знал, что теперь его rotation равен 3. А ImageAnalysis продолжал жить со старым:
ImageProxy.rotationDegrees вслед за этим тоже оставался 0.
Теперь у нас была не гипотеза «похоже, CameraX иногда запаздывает», а конкретная измеренная причина: при перевороте на 180° ImageAnalysis.targetRotation не обновлялся.
Решение без ожиданий и rebind
После этого исправление стало заметно проще первоначального workaround.
Было направление примерно такого вида:
Стало:
Существующие Preview и ImageAnalysis при этом не пересоздаются.
Никаких:
Ещё обнаружилась неочевидная деталь: градусы, которые сообщает OrientationEventListener, нельзя напрямую трактовать как одноимённый Surface.ROTATION_*. В нашем случае диапазон 45..134° должен был приводить к ROTATION_270, а не к ROTATION_90.А затем снова тот же физический эксперимент.
До:
Переворачиваю планшет.
После:
То есть цепочка наконец стала согласованной.
CameraX получил новый targetRotation, а ImageProxy.rotationDegrees изменился с 0 на 180.
И всё это без пересоздания Preview/ImageAnalysis и без rebind.
Но ведь агент сначала предложил неправильное решение
Да. И для меня это как раз самая интересная часть этой маленькой задачи.
Можно посмотреть на историю и сказать: «AI ошибся, значит ему нельзя доверять».
Можно сделать противоположный вывод: «AI всё починил».
Мне не подходит ни один.
Агент сделал огромную часть работы. Он читал существующий код, добавлял диагностику, собирал данные с устройства, запускал adb, анализировал логи, менял реализацию, повторно проверял результат и в конце обновил MR.
Я действительно почти не занимался механическим написанием этого исправления.
Но в один момент агент начал оптимизировать не ту модель проблемы: раз rotation появляется позже --- значит надо научиться его ждать.
Моя роль была не в том, чтобы быстрее него написать правильные пять строк Kotlin. Я их в тот момент даже не знал.
Моя роль была сказать: нет, сначала объясни, почему это происходит.
После этого направление расследования изменилось.
Если грубо разделить работу, получилось так:
Что я из этого вынес
Мне кажется, разговор о coding agents часто сводится к вопросу: «может ли AI написать этот код вместо программиста?»
На практике для меня всё интереснее.
В этой задаче ценность агента была не в генерации нескольких строк с OrientationEventListener. Ценность была в том, что цикл:
можно было практически полностью отдать ему.
А моя работа сместилась уровнем выше:
При этом coding agent не стал безошибочным. Наоборот, этот кейс хорошо показал, почему бездумное «почини баг» может закончиться вполне работающим postDelayed, который потом проживёт в проекте несколько лет.
Мне всё меньше приходится быть человеком, который вручную выполняет каждый технический шаг. Но необходимость понимать, какой эксперимент мы сейчас проводим, что именно он доказывает и можно ли доверять полученному решению, никуда не исчезла.
Пожалуй, именно так для меня сейчас и выглядит разработка с coding agents.