Баги из рабочей практики
Самая непредсказуемая часть любого приложения – человек за клавиатурой. Нам предопределено хлебать смузи и думать о вечном, сидя на безусловном базовом мешке. А мы ломаем машинные процессы из-за своей иррациональности.
Но пока, именно благодаря ошибкам я открыл для себя автоматические тесты, обязательное код-ревью и канареечную раскатку.
Как баги всплывают на поверхность:
• отловил самостоятельно в процессе разработки
• упал автоматический тест
• не прошли код-ревью
• тестировщик вернул задачу
• прилетело из крэшлитики
• аудит или отчет безопасников
• обратная связь от пользователей
Также пришлось поработать над двумя соцсетями. В каждой был подсайт или публичный топик, где юзеры описывали баги. Эти посты превращались в джира-тикеты. На одной работе был канал в слаке, куда сыпались оценки с отзывами из Google Play. Если ставили одну звезду, кабаныч тегал нас под сообщением.
А вот и сами баги:
1. В старых версиях Android Studio линтер не сообщал, что метод setForeground появился в Android 6. Вызвал его без проверки уровня API и приложение грохнулось в рантайме на Android 5. Так я узнал, что у нас еще есть такие пользователи.
2. На стартовом экране был какой-то сетевой запрос. Обработку ошибок для него не сделали. Однажды он отвалился совсем. Юзеры запускали апп, пялились на сплэшскрин и не могли навигироваться дальше.
3. Неправильно настроил launchMode и интенты. У пользователей в Recent Menu отображались две копии нашего приложения.
4. Был экран на WebView, замаскированный под нативный интерфейс – позже его собирались переделать. Ссылки с этого экрана должны были открываться в Chrome Custom Tabs, но из-за сломанного перехватчика shouldOverrideUrlLoading открывались в самом WebView.
5. Приложение оперировало квантами, штуками, рублями и копейками. Из-за неправильного округления итоговая сумма расходилась на 1 копейку. Запрос возвращал ошибку. Флоу блокировался. Познакомился с BigDecimal и roundToInt.
6. Забыл удалить моковые данные. Бывает бэк прилег, а разработка должна продолжаться. Повесил на кнопку навигацию на экран, недоступный без интернета. В запрос добавил delay(10_000), чтобы протестировать лоадинг. Моки улетели в репозиторий.
7. Метод бана юзера в API с параметром duration ожидал либо -1 (пермобан), либо таймстамп в секундах. Из-за kotlin nullable в одной из функций значение «на день» превратилось в -1 и юзеры забанились навсегда.
8. Не добавил обработку http-кода 204 и схлопотал NoSuchElementException. Запрос выполнился, но выбранная сигнатура была несовместима с пустым телом.
9. Выпустили релиз с NullPointerException: поспешно добавил ViewBinding в новые фрагменты, не прочитав документацию.
10. Включение вспышки срабатывало с заметной задержкой, потому что вызывало рекомпозицию и пересоздание инстанса камеры. Затащить камеру в Compose было непросто, поскольку API ориентирован на систему View.
11. На экране поста в заголовках добавлялась иконка галочки в конце. Иногда она переносилась отдельно на новую строку, отчего у дизайнера дергался глаз. Я не придумал ничего лучше, чем объединить ее с последним словом: вынул слово, объединил с иконкой в Span, вернул обратно. Работало прекрасно, пока не вышла статья с очень длинным последним словом в заголовке. Контент уехал за пределы экрана.
12. Делал редактор текста с поддержкой markdown: курсив, жирный, жирный курсив и так далее. Неожиданно в сервисе запретили мат. Пользователи стали маскировать его многочисленными звездочками, к чему редактор оказался не готов. Разметка пустилась в пляс.
13. Поехала верстка из-за того, что какая-то библиотека затащила самописный ConstraintLayout. Исключил транзитивную зависимость через exclude.
14. Мутные операторы котлин-коллекций: all, any и none ведут себя по-разному на пустых коллекциях, что провоцирует логические ошибки: all{…} отдаёт true за счёт «вакуумной истины», когда нет ни одного потенциального контрпримера. any() возвращает false, а none() – true. Я уже несколько раз на этом обжигался. Еще и курсор любит неудачно применять эти функции в коде.
Самая непредсказуемая часть любого приложения – человек за клавиатурой. Нам предопределено хлебать смузи и думать о вечном, сидя на безусловном базовом мешке. А мы ломаем машинные процессы из-за своей иррациональности.
Но пока, именно благодаря ошибкам я открыл для себя автоматические тесты, обязательное код-ревью и канареечную раскатку.
Как баги всплывают на поверхность:
• отловил самостоятельно в процессе разработки
• упал автоматический тест
• не прошли код-ревью
• тестировщик вернул задачу
• прилетело из крэшлитики
• аудит или отчет безопасников
• обратная связь от пользователей
Также пришлось поработать над двумя соцсетями. В каждой был подсайт или публичный топик, где юзеры описывали баги. Эти посты превращались в джира-тикеты. На одной работе был канал в слаке, куда сыпались оценки с отзывами из Google Play. Если ставили одну звезду, кабаныч тегал нас под сообщением.
А вот и сами баги:
1. В старых версиях Android Studio линтер не сообщал, что метод setForeground появился в Android 6. Вызвал его без проверки уровня API и приложение грохнулось в рантайме на Android 5. Так я узнал, что у нас еще есть такие пользователи.
2. На стартовом экране был какой-то сетевой запрос. Обработку ошибок для него не сделали. Однажды он отвалился совсем. Юзеры запускали апп, пялились на сплэшскрин и не могли навигироваться дальше.
3. Неправильно настроил launchMode и интенты. У пользователей в Recent Menu отображались две копии нашего приложения.
4. Был экран на WebView, замаскированный под нативный интерфейс – позже его собирались переделать. Ссылки с этого экрана должны были открываться в Chrome Custom Tabs, но из-за сломанного перехватчика shouldOverrideUrlLoading открывались в самом WebView.
5. Приложение оперировало квантами, штуками, рублями и копейками. Из-за неправильного округления итоговая сумма расходилась на 1 копейку. Запрос возвращал ошибку. Флоу блокировался. Познакомился с BigDecimal и roundToInt.
6. Забыл удалить моковые данные. Бывает бэк прилег, а разработка должна продолжаться. Повесил на кнопку навигацию на экран, недоступный без интернета. В запрос добавил delay(10_000), чтобы протестировать лоадинг. Моки улетели в репозиторий.
7. Метод бана юзера в API с параметром duration ожидал либо -1 (пермобан), либо таймстамп в секундах. Из-за kotlin nullable в одной из функций значение «на день» превратилось в -1 и юзеры забанились навсегда.
8. Не добавил обработку http-кода 204 и схлопотал NoSuchElementException. Запрос выполнился, но выбранная сигнатура была несовместима с пустым телом.
9. Выпустили релиз с NullPointerException: поспешно добавил ViewBinding в новые фрагменты, не прочитав документацию.
10. Включение вспышки срабатывало с заметной задержкой, потому что вызывало рекомпозицию и пересоздание инстанса камеры. Затащить камеру в Compose было непросто, поскольку API ориентирован на систему View.
11. На экране поста в заголовках добавлялась иконка галочки в конце. Иногда она переносилась отдельно на новую строку, отчего у дизайнера дергался глаз. Я не придумал ничего лучше, чем объединить ее с последним словом: вынул слово, объединил с иконкой в Span, вернул обратно. Работало прекрасно, пока не вышла статья с очень длинным последним словом в заголовке. Контент уехал за пределы экрана.
12. Делал редактор текста с поддержкой markdown: курсив, жирный, жирный курсив и так далее. Неожиданно в сервисе запретили мат. Пользователи стали маскировать его многочисленными звездочками, к чему редактор оказался не готов. Разметка пустилась в пляс.
13. Поехала верстка из-за того, что какая-то библиотека затащила самописный ConstraintLayout. Исключил транзитивную зависимость через exclude.
14. Мутные операторы котлин-коллекций: all, any и none ведут себя по-разному на пустых коллекциях, что провоцирует логические ошибки: all{…} отдаёт true за счёт «вакуумной истины», когда нет ни одного потенциального контрпримера. any() возвращает false, а none() – true. Я уже несколько раз на этом обжигался. Еще и курсор любит неудачно применять эти функции в коде.