Ваше Android-приложение как место преступления
Технический аудит iOS и Android приложений стал неотъемлемой частью нашей повседневной работы в Karumi. Хоть это и выглядит просто, имеется немало деталей реализации такой проверки, которые стоит рассмотреть. В этом документе мы рассмотрим то, что мы считаем наиболее важным при проведении аудита и разделим это по техническим областям.
Система контроля версий
Используется ли система контроля версий, какая система и как организованы рабочие процессы - это многое расскажет о процессе разработки.
-
Есть ли у вас правильно настроенный *ignore файл, чтобы файлы метаданных IDE и другие посторонние элементы не попадали в хранилище?
-
Сторонние библиотеки сконфигурированы в качестве внешних зависимостей или лежат в хранилище?
-
Достаточно ли краткие и выразительные комментарии к коммитам (commits) вы используете?
-
Является ли размер ваших коммитов обоснованным?
-
Все ли файлы в коммите связаны с одной и той же проблемой или функциональностью?
-
Используете ли вы какие-либо схемы ветвления типа “feature branch” или “git-flow”?
-
Достаточно ли информативны названия веток?
-
Используете ли вы систему pull request/code review до слияния кода в master?
-
Есть ли у вас какой-либо регламент относительно того, на что нужно обращать внимание при рассмотрении PR (pull request)?
-
Сколько комментариев в среднем приходится на каждый PR?
-
Сколько людей рассматривает каждый PR?
-
Сколько “+1” к PR вам нужно для слияния?
-
Кто несет ответственность за закрытие ветки?
-
-
Вы используете release branches для каждого релиза?
-
Как долго открыт процесс подготовки (staging process)?
-
Сколько исправлений вы вносите в release candidate, прежде чем выпустить его?
-
Имеется ли возможность переключиться на точный код какой-либо из опубликованных версий вашего приложения?
-
Сколько исправлений (hotfix) вы выпустили в прошлом году?
-
Вы объединяете (squash) коммиты до слияния (merging) его в master/develop ветку?
-
Готовы ли master/develop ветки к выпуску в любое время?
Инструменты сборки
Определяющим фактором является возможность запустить процесс сборки на машине разработчика и на любой другой внешней системе, например на системе непрерывной сборки (continuous integration).
-
Сколько библиотек используются в проекте?
-
Разделен ли проект на модули?
-
Используют ли модули Maven или Gradle для разрешения зависимостей, или используются локальные .jar-файлы?
-
На опасное ли расстояние приближен проект к лимиту количества методов в .dex файлах? Или уже за гранью?
-
Вы используете библиотеки, которые не нужны в проекте?
-
Используется ли multidex?
-
Все ли внешние зависимости обновлены до современных версий?
-
Все ли лицензии сторонних библиотек соблюдены?
-
Используются ли устаревшие или не поддерживаемые сторонние библиотеки?
-
Обусловлен ли минимальный SDK требованиями технического задания (product description)?
-
Актуален ли целевой SDK (target SDK)?
-
Используется ли ProGuard или любой другой инструмент обфускации (obfuscation - запутывание)? Он включен и правильно настроен?
-
Учетные данные хранилища ключей (keystore credentials) и учетные данные Google Play хранятся в надежном месте?
-
Хранилище ключей приложения (application keystore) и учетные данные хранятся в надежном месте?
-
Должным ли образом настроены build types?
-
Правильно ли используются flavors?
-
Правильно ли настроен релизный тип сборки?
-
Включена ли опция резервного копирования?
-
Включен ли Lint? Он успешно проходит проверку?
-
Имеется ли инструмент статического анализа? Он настроен и успешно проходит проверку?
-
Есть ли Checkstyle? Он настроен и успешно проходит проверку?
-
Правильно ли настроен id приложения и version name/code?
-
Вы используете какую-либо структуру или стратегию версионирования id?
-
Используется ли инструмент непрерывной сборки (continous integration), правильно ли он настроен?
-
Автоматизирован ли процесс выпуска новых версий?
Использование Android ресурсов
Существует широкий спектр устройств в мире Android, каждый из них со своим собственным размером экрана, возможностями и т.д. Вам нужно быть очень внимательными и осторожно использовать некоторые из Android инструментов, чтобы у пользователей остались наилучшие впечатления от вашего приложения вне зависимости от их устройства.
-
Существуют ли какие-либо недостающие ресурсы для densities, flavors и build types?
-
Требуется ли поддержка приложением экранов с различной плотностью пикселей (density) по техническому заданию?
-
Используются ли в приложении drawable/mipmap, шрифты или векторные ресурсы?
-
Существуют ли какие-либо недостающие переводы?
-
Автоматизирован ли процесс перевода?
-
Какой язык выбран по умолчанию для перевода?
-
Использует ли приложение сторонние шрифты?
-
Использует ли приложение значения конфигурации внутри файла строковых ресурсов?
-
Соблюдается ли соглашение для присвоения однородных имен ресурсам?
-
Есть ли параметры конфигурации, связанные с аппаратными средствами устройства, правильно ли они настроены?
-
Поддерживаются ли планшеты?
Использование Android Layout
Как мы уже говорили ранее, существует широкий спектр Android устройств в мире, каждый из них с собственным размером и плотностью экрана. Определяющим фактором является правильное использование Android Layouts.
-
Возникают ли проблемы с производительностью из-за количество слоев в макетах (layouts) приложения?
-
Используете ли вы темы и стили?
-
Используются ли повторно макеты (layouts) с использованием “include” тега?
-
Вы используете правильный тип группировок в макетах?
-
Учтены ли различные размеры экранов в макетах?
-
Используется ли какое-либо соглашение об именовании, чтобы присваиваемые имена макетам и виджетам оставались однородными?
-
Списки реализованы с использованием ListView или RecyclerView?
-
Правильно ли используется Android Support Library?
Права доступа
Запрос возможных действий приложения (permissions) увеличивает доверие пользователей к нему, а также расширяет его возможности за счет “прозрачной” интеграции с другими сервисами.
-
Все ли запрашиваемые разрешения (permissions) действительно необходимы?
-
Разрешение используется намеренно?
-
Есть ли отсутствующие разрешения?
-
Если целевой SDK больше, чем 23, то “опасные разрешения” запрашиваются с помощью системы разрешений совместимости (compatibility permissions system)?
-
Разрешение запрашиваются тогда, когда они будут использоваться?
-
Есть ли обратная связь с пользователем, объясняющая, почему какое-либо разрешение необходимо?
Проблемы с безопасностью
Как разработчики, мы должны сознательно относиться к безопасности наших приложений. Мы не хотим, чтобы данные наших пользователей “утекли” или их сессии были украдены.
-
Настроен ли HTTP клиент на использование HTTPS?
-
Настроен ли HTTP клиент на использование встроенного сертификата (certificate pinning) и сообщений аутентификации с HMAC?
-
Сохраняет ли приложение конфиденциальную информацию пользователя? Где?
-
Сохраняет ли приложение информацию вне внутренней системы хранения данных?
-
Логируется (logging traces) ли приложение при сборке релиза?
-
Обфусцирован (obfuscated) ли код приложения?
-
Предоставляет ли ваше приложение поставщика контента (Android content provider), приемника (receiver) или сервис другим приложениям?
-
Отключена ли опция “debuggable” в релизной сборке?
Push уведомления (Push Notifications)
Push - это отличный механизм для информирования наших пользователей в любое время, но это более сложная проблема, чем кажется на первый взгляд.
-
Используется ли сторонняя библиотека для реализации системы Push-уведомлений?
-
Используется ли система GCM для передачи информации приложению, или просто, чтобы показывать сообщения пользователю?
-
Как ведет себя приложение при получении Push-уведомления?
-
Как ведет себя приложение, когда связанная с Push-уведомлением информация не та, что ожидалась?
-
Показываются ли уведомления с использованием API совместимости (compatibility API)?
Производительность
Производительность имеет большое значение. Никто не захочет использовать на своих дорогих устройствах медленное приложение. Производительность - это деньги.
-
Есть ли в приложении какие-либо утечки памяти?
-
Настроен ли в develop сборке какой-либо анализатор памяти, как, например, “LeakCanary”?
-
Android Strict Mode подключен и настроен в develop сборке?
-
Как в приложении используются потоки? Вы используете async tasks, intent services или любые другие сторонние библиотеки?
-
Вызывает ли количество фоновых потоков проблемы с производительностью?
-
Используете ли вы какие-либо политики планировщика (scheduler policy) или просто создаются потоки по требованию?
-
Поддерживаете ли вы Android Doze Mode?
-
Прослушиваются ли события, связанные с состоянием сети или любого другого повторяющегося события от операционной системы?
-
Основной поток используется только для выполнения задач, связанных с кодом пользовательского интерфейса?
-
Имеются ли в приложении какие-либо политики кеширования?
-
Настроен ли клиент HTTP на использование тайм-аута?
-
Настроен ли клиент HTTP на использование GZIP?
-
Работает ли пользовательский интерфейс приложения со скоростью 60 кадров в секунду?
-
Нет ли таких custom view, для которых выделяется слишком большой объем памяти, или которые выполняют “дорогостоящие” задачи в потоке пользовательского интерфейса (UI thread)?
-
Вы тестируете ваше приложение на lower-end (бюджетных, непроизводительных) устройствах?
-
Не “тормозит” ли прокрутка recycler view?
-
Для загрузки изображений используется какая-либо сторонняя библиотека или у вас есть своё собственное решение?
-
Изображения масштабируются под размеры экрана или сразу загружаются под определенный экран устройства?
-
Разумно ли использование памяти?
-
Правильно ли используется модификатор “Static” в Java?
-
Все ли задачи, связанные с обработкой изображений, могут обрабатывать несколько изображений за раз?
-
Работает ли система статистики в фоновом потоке и сконфигурирована ли она с правильным приоритетом?
-
Оптимизируется ли код в релизной сборке?
Структура Java Packages
Хорошая структура пакетов сделает наш код более масштабируемым.
-
Используются ли пакеты для разделения кода по функционалу (features) или концепциям (например, Login vs User)?
-
Используются ли модификаторы видимости Java, чтобы скрыть детали реализации внутри пакетов?
-
Все ли пакеты выходят из корневого пакета?
-
Повторяет ли директория с тестами структуру исходной папки?
-
Организованы ли различные функции (features) с помощью одной и той же структуры пакетов?
-
В правильных ли пакетах лежат классы?
-
Соблюдаются ли соглашения об однородном названии пакетов?
-
Связано ли название корневого пакета с именем компании?
Code Style
Согласованная с точки зрения стиля кодовая база помогает нашим инженерам читать код проще. Инженер читает ГОРАЗДО больше кода, чем пишет, так что это важное понятие.
-
Является ли codestyle однородным?
-
Используете ли вы венгерскую нотацию?
-
Есть ли какой-либо инструмент Checkstyle? Он настроен и работает?
-
Соответствует ли код Java codestyle?
-
Вы используете табуляцию или пробелы?
-
Правильно ли названы классы?
-
Используете ли вы “I” в качестве префикса интерфейсов или “Impl” как суффиксы реализации?
-
Используете ли вы правильные имена переменных?
-
Используете ли вы правильные имена для полей (fields)?
-
Используете ли вы правильные имена для методов?
-
Используются ли атрибуты и модификаторы видимости методов должным образом?
-
Код написан на английском языке?
-
Используете ли вы Javadoc?
-
Вы пишете комментарии к коду?
-
Используете ли вы константы или перечисления (enums), чтобы избежать дублирования литералов?
Offline реализация
Обеспечение хорошей работы в режиме offline является отличительным фактором наших приложений.
-
Может ли приложение использоваться, когда нет подключения к сети Интернет?
-
Каково поведение приложения при медленном сетевом соединении?
-
Какое поведение приложения при потере запроса из-за сбоя в сети?
-
Синхронизируются ли изменения данных приложения с бекэндом после того как соединение было восстановлено?
-
Настроен ли тайм-аут на сетевое соединение?
-
Настроена ли политика кеширования HTTP?
-
Сеанс пользователя восстанавливается автоматически?
Архитектура
Архитектура приложений, с точки зрения кода, является одной из частей аудита, которая дает нам более глубокое представление о приложении. В ходе обзора архитектуры приложения мы будем сосредоточены на понятиях, связанных с S.O.L.I.D и Clean Code принципами.
Реализация слоя представления (Presentation Layer Implementation)
-
Используется ли какой-либо паттерн, связанный с реализацией GUI (графического интерфейса пользователя)? Model View Presenter или Model View ViewModel два из наиболее часто используемых шаблонов для разработки приложений. Правильно ли они реализованы?
-
Связана ли реализация слоя представления (presentation layer) с реализацией вида (view implementation)?
-
Связана ли реализация вида (view implementation) c реализацией модели (model implementation)?
-
Включает ли слой представления бизнес-логику?
-
Правильно ли реализация вида использует Android SDK tools?
-
Используете ли вы сторонние библиотеки для упрощения реализации вида?
-
Разделена ли реализация разных функций по отдельным activities или fragments?
-
Однородно ли поведение пользовательского интерфейса?
-
Используете ли вы custom views для повторного использования кода пользовательского интерфейса?
Реализация предметной области (Domain Implementation)
-
Есть ли отдельный слой предметной области (domain layer) или вся бизнес-логика реализована в слое представления (presentation layer)?
-
Выражены ли правила предметной области и различные требования к приложению в основных объектах бизнес-логики?
-
Реализован ли слой предметной области с применением принципов ООП?
-
Связан ли слой предметной области с Android SDK или любой сторонней библиотекой?
-
Связан ли слой предметной области со слоем представления?
-
Является ли модель предметной области (domain model) “анемичной”?
-
Используете ли вы “толстые” модели предметной области?
-
Основан ли код на слабо связанных и, в то же время, хорошо взаимодействующих друг с другом компонентах?
-
Реализована ли обработка ошибок с использованием исключений или какого-либо другого механизма обработки ошибок?
-
Сопоставляются ли данные (mapped) между различными слоями?
-
Влияет ли архитектура внешних компонентов (например, схемы базы данных или парсинга JSON) на архитектуру модели предметной области?
-
Разработчики злоупотребляют наследованием?
-
Дублируется ли код?
-
Есть ли dependency injection библиотеки или сконфигурированный service locator?
-
Не слишком ли высока сложность классов и методов?
Реализация API
-
Привязана ли реализация API к Android SDK?
-
Происходит ли “утечка” через API каких-либо деталей реализации из-за клиента HTTP или используемой библиотекой для реализации сетевого слоя?
-
API клиент посылает правильные заголовки?
-
Каково поведение API клиента для различных ответов HTTP?
-
API клиента реализует механизм аутентификации?
-
Правильно ли реализован процесс возобновления сеанса?
-
Есть ли поддержка обфускации JSON?
-
Разделена ли реализация API для различных клиентов?
Реализация Хранения
-
Где хранится информация?
-
Используются ли транзакции при чтении и записи информации в хранилище?
-
Надежно ли хранение конфиденциальной информации пользователя?
-
Использует ли слой хранения какие-либо сторонние библиотеки?
-
“Утекают” ли детали реализации через слой хранения?
-
Правильно ли спроектированы tables/schemas?
-
Запросы, отправленные в хранилище, оптимизированы?
-
Используются ли Android SDK APIs для хранения данных в правильном месте? Сохраняются ли данные в базу данных, а настройки (preferences) и небольшие по объему данные в Shared Preferences и файлы на диске?
Тестируемость
-
Есть ли у приложения тесты?
-
Является ли приложение тестируемым?
-
В приложении используются различные виды (unit/integration/end-to-end) тестов?
-
Правильно ли названы тесты?
-
Достаточно ли тесты охватывают проект?
-
Есть ли чрезмерное документирование (overspecification) в тестах?
-
Время выполнения разумно?
-
Не слишком ли низко покрытие кода?
-
Существуют ли какие-либо игнорируемые тесты?
-
Есть ли нестабильные тесты (flaky tests)?
-
Используете ли вы современные фреймворки для тестирования?
-
Существуют ли какие-либо тесты без утверждений (tests without asserts)?
-
Тесты и продакшен код написаны одними и теми же разработчиками?
-
У вас есть команда QA?
-
У вас есть команда QA, автоматизирующая часть тестов?
-
Есть ли у вас какие-либо системы непрерывной интеграции (continuous integration system)?
-
Используете ли вы builders, factories или mothers, чтобы уменьшить затраты на создание некоторых объектов, которые необходимы для тестов?
-
Тесты утверждений (assertions) правильно написаны?
-
Проводите ли вы более одного логического утверждения на тест?
-
У вас есть различные наборы тестов, связанные с одним и тем же проектом?
-
Вы используете различные подходы тестирования для различных частей приложения?
-
Используете ли вы какой-либо monkeyrunner?
-
Следуете ли вы какой-либо TDD или BDD методологии?
-
Используете ли вы Java, чтобы написать тестовые варианты (test cases)?
На основании этого списка, связанного с различными техническими областями, мы можем оценить качество приложения. Есть и другие моменты, которые мы также рассматриваем, но этот список содержит наиболее важные из них. Вы можете дать корректные ответы на все эти вопросы о вашем приложении?
Автор: Pedro Vicente Gómez Sánchez.