Когда я работал в одном стартапе, мы три месяца не делали code review. Просто сливали фичи в main, и если что-то падало — узнавали об этом от пользователей. Потом пришлось переделывать половину архитектуры. Теперь я знаю: проверка кода мобильных приложений — это не формальность, это защита.
Но это не просто просмотр pull request'ов в GitHub. Это целая система: от настройки процесса в команде до автоматизации, которая ловит баги до того, как код попадёт в руки тестировщикам.
Зачем вообще нужна проверка кода в мобильной разработке
Давайте честно. На мобильном в разы больше факторов, чем на веб. Разные версии ОС, устройства, разрешения, батарея, интернет. Каждый баг в мобильной app'е — это потенциально 100 тысяч пользователей, которые сразу получат обновление через App Store или Google Play.
По моему опыту, code review в мобильной разработке ловит:
Утечки памяти — самая коварная проблема. На Android легко создать reference cycle через view или listener. На iOS забыл [weak self] в блоке — и лекло. Приложение будет работать нормально на тестовом девайсе неделю, а потом начнёт жрать память и крашиться у пользователей.
Race conditions и deadlock'и — особенно в многопоточном коде. На мобильном это критично: сетевой запрос в фоне, UI обновляется на main thread, БД пишется в отдельном потоке. Если не синхронизировать правильно, получишь крэши, которые воспроизводятся один раз из ста.
Нарушения guideline'ов платформы — App Store и Google Play отклонят приложение, если ты неправильно запрашиваешь разрешения, неправильно работаешь с батареей или нарушаешь privacy policy. Лучше узнать об этом на code review, чем после отправки на модерацию.
Проблемы с производительностью — лагающий скролл, долгие анимации, медленный запуск app'а. На мобильном всё это сразу видно. На проверке кода можно поймать неправильный layout, бесполезные вычисления в главном потоке или утечку батареи.
Короче, code review на мобильном — это не про стиль кода, это про то, что app будет работать нормально и не будет крашиться у пользователей.
Как организовать процесс code review в команде
На одном проекте я видел, как разработчик два часа ждал, пока кто-то посмотрит его PR. На другом — все друг друга игнорили, и обзоры накапливались. Третий вариант — один senior всё проверял, и он стал узким местом.
Вот что работает:
Парам нужны друг за другом идущие ревьюеры. Я пишу код, отправляю PR, и сразу видно: кого просить. Не "кто-нибудь посмотрите", а "прошу review у Максима и Ксении". Так быстрее, и ответственность явная.
SLA на ревью — от 2 до 4 часов. Если код лежит сутки, разработчик уже забыл, что там писал. Если ревьюер смотрит за 20 минут, он пропустит половину проблем. Два часа — оптимально. Критичные fixes смотрим быстрее.
Ревью должен делать тот, кто не писал код. Банально, но работает. Ты пишешь код, и тебе кажется, что всё понятно. Когда другой человек читает — сразу видны запутанные куски.
Минимум одобрение перед мёржем. На мобильном я бы даже сказал — два. Особенно если меняешь архитектуру или критичные части.
На практике процесс выглядит так:
Разработчик пишет фичу в отдельной ветке
↓
Создаёт PR/MR с описанием (что изменилось, почему)
↓
Назначает ревьюеров (желательно двух)
↓
Ревьюер смотрит, оставляет комментарии
↓
Разработчик пушит исправления в ту же ветку
↓
Ревьюер проверяет ещё раз
↓
Approval → мёрж в develop
Чеклист для code review мобильного приложения
Когда ревьюишь мобильный код, легко потеряться. Я веду простой чеклист. Его можно кастомизировать под твой проект, но базис одинаков везде.
Архитектура и дизайн
- Код следует архитектуре проекта (MVVM, Clean, Redux — неважно, главное консистентность)
- Нет циклических зависимостей между модулями
- Новые фичи не создают god-объекты, которые всё знают и всё делают
- Если добавлена новая абстракция — она действительно нужна, не просто "на будущее"
Многопоточность и асинхронность
- UI обновляется только на main thread (UIThread на Android, main queue на iOS)
- Все сетевые запросы в фоновом потоке
- Нет deadlock'ов и race condition'ов
- Правильное управление жизненным циклом корутин, RxJava subscriptions или Combine publishers
- При отмене операции (back, закрытие экрана) корректно очищаются ресурсы
Память
- Нет очевидных утечек (сохранение ссылок на context, view, activity в статических полях)
- В замыканиях и callback'ах используются weak ссылки где нужно
- Bitmap'ы и большие объекты очищаются после использования
- Listener'ы unsubscribe'ятся в onDestroy/deinit
Безопасность
- Чувствительные данные (пароли, токены) не логируются и не хранятся в SharedPreferences в открытом виде
- API ключи не закомичены в репо (должны быть в env или конфиге)
- Используется HTTPS для всех сетевых запросов
- Входные данные валидируются перед использованием
Сеть и производительность
- Запросы кэшируются где уместно (не дёргаем API каждый раз)
- Нет N+1 запросов (когда мы в цикле дёргаем API для каждого элемента)
- Большие операции (БД запросы, парсинг JSON) не блокируют UI
- Изображения масштабируются под размер экрана, не грузятся в full resolution
- Нет утечек батареи (частые GPS запросы, постоянный Bluetooth, wake lock'и)
Всё это звучит много, но на практике большую часть проверяет автоматика. Я к этому ещё вернусь.
Типичные ошибки при code review мобильных приложений
Ревьюер смотрит только на стиль кода. Пробелы, скобки, имена переменных — всё это важно, но не главное. Главное — работает ли это? Не будет ли крашиться? Не сожрёт ли батарею?
На одном проекте senior ревьюер час ловил несовместимость Android API level'ей, но совершенно прошёл мимо утечки памяти в listener'е. Потому что утечка не видна с первого взгляда, а несовместимость — в одной строке.
Смотрят изолированно, без контекста. Код красивый, но как он интегрируется с остальной системой? Не дублирует ли он логику из другого модуля? На какие сторонние библиотеки зависит?
Я видел, как разработчик добавил новый способ кэширования, но уже существовал старый. Теперь в приложении два разных механизма кэша, они конкурируют, и никто не знает, какой использовать.
Не тестируют PR локально. Самый быстрый способ поймать проблемы — запустить код. Посмотреть, как работает фича, поломать её, проверить edge case'ы.
Ревью слишком долгое. Если PR больше 500 строк, человек просто не может качественно проверить. Я ловил баги в PR'ах по 50 строк, где я потратил 20 минут. В PR'ах по 2000 строк люди скользят по поверхности.
Общие комментарии вместо конкретных. "Это можно сделать лучше" — это не помогает. Лучше: "Здесь у нас есть утечка памяти, потому что view сохраняется в статическом поле. Нужно либо использовать weak reference, либо очищать в onDestroy".
Автоматизация проверки кода
Честно? Большинство ошибок в коде ловят инструменты, а не люди.
На Android это Lint, Detekt, FindBugs — они ловят кучу проблем автоматически. На iOS — SwiftLint, Clang analyzer. На JavaScript/TypeScript (если пишешь React Native) — ESLint, Prettier.
Вот что я обычно настраиваю в CI/CD:
# Пример для GitHub Actions
name: Code Review
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Detekt
run: ./gradlew detekt
- name: Run Lint
run: ./gradlew lint
- name: Check security
run: ./gradlew dependencyCheck
На iOS похоже:
# swiftlint + clang analyzer в Xcode
swiftlint lint --reporter json > lint-report.json
xcodebuild analyze -scheme MyApp -derivedDataPath build
Но вот в чём подвох: инструменты находят синтаксические ошибки, нарушения стиля, очевидные баги. Они не понимают бизнес-логику, не видят race condition'ов в сложном многопоточном коде, не знают, правильно ли ты кэшируешь данные.
Это работа для человека.
На практике я использую такую схему:
- Автоматика проверяет — style, security, очевидные баги
- CI не пускает PR в основную ветку, если автоматика не прошла
- Человек смотрит на логику — архитектура, производительность, корректность
Если автоматика прошла, я трачу 80% времени на логику, а не на "отступы неправильные".
Практические советы из опыта
Документируй нетривиальное. Если ты делаешь что-то, что не очевидно с первого взгляда, напиши комментарий. Зачем? Потому что. Например:
// Используем volatile вместо AtomicBoolean, потому что нам нужна только видимость,
// а не atomicity операций. AtomicBoolean создаёт лишний object на каждый check.
@Volatile
private var isLoading = false
Дели большие фичи на маленькие PR'ы. Если ты пишешь новый экран — не пушь сразу весь экран в один PR. Сначала архитектура, потом UI, потом логика. Так быстрее ревьюить, и ошибки видны лучше.
Пиши хорошее описание в PR. "Fixed bug" — это ничего не говорит. "Исправлена утечка памяти в listener'е: он сохранял ссылку на Activity через анонимный класс. Добавлена слабая ссылка и очистка в onDestroy" — это говорит всё.
Если ревьюер не согласен с твоим решением — обсудите. Не "ты неправ, я так делаю". А "я вижу твою точку зрения, но есть компромисс: можем сделать так". Иногда ревьюер не знает контекст, и ты его просвещаешь. Иногда ты неправ, и нужно переделать.
Делай ревью для себя. Когда я смотрю чужой PR, я представляю, как я буду этот код менять через месяц. Понятно ли будет? Не сломаю ли я случайно что-нибудь?
Инструменты для автоматической проверки кода
Вот что я беру на новые проекты:
- SonarQube / SonarCloud — анализирует баги, уязвимости, code smell'ы
- Detekt (Kotlin) — находит проблемы с кодом, которые Lint пропускает
- SwiftLint (iOS) — style guide автоматически
- ESLint + Prettier (JavaScript/TypeScript) — форматирование и проверка
- Dependabot — следит за устаревшими зависимостями
- OWASP Dependency-Check — ищет известные уязвимости в библиотеках
Но есть нюанс. Все эти инструменты настраиваются вручную, нужно смотреть логи, парсить результаты. А что, если есть инструмент, который сам оставляет комментарии в PR'е?
Мы в Distiq как раз это делаем. AI анализирует каждый PR и оставляет инлайн-комментарии там, где найдёт проблемы. Поддерживает Python, JavaScript
