Skip to main content

Android Code Review Checklist

Rapid-scan checklist for Android code reviews. Use this to quickly identify issues, then dive into linked documentation for details.


Security Red Flags - Block PR Immediately

  • Plain SharedPreferences for secrets: Tokens, API keys in plain SharedPreferences → Use EncryptedSharedPreferences
  • Hardcoded secrets: API keys, passwords in source code → Use BuildConfig or secure storage
  • SQL injection: String concatenation in SQL/Room queries → Use parameterized queries
  • No certificate pinning: Production API without certificate pinning → Certificate Pinning
  • Sensitive data in logs: Tokens, account numbers, PINs in Logcat → Never log sensitive data
  • Weak biometric authenticators: BIOMETRIC_WEAK instead of BIOMETRIC_STRONG
  • ProGuard/R8 disabled: Release builds without obfuscation
  • Root/jailbreak detection bypassed: No SafetyNet/Play Integrity checks for financial transactions
  • Screenshots enabled on sensitive screens: No FLAG_SECURE on payment/auth screens
  • Network cleartext traffic allowed: usesCleartextTraffic="true" in manifest

Common Mistakes

Architecture Violations

Dependency Injection (Hilt)

  • Field injection instead of constructor injection
  • @Singleton scope for stateful objects (leaks)
  • Manual instantiation of dependencies (bypasses Hilt)
  • Wrong component scope (SingletonComponent for ViewModels)
  • Missing @HiltViewModel annotation on ViewModels
  • Not using @Inject constructor for automatic provision

Jetpack Compose Issues

  • Reading state in composition without remember (recomposition leak)
  • Side effects not in LaunchedEffect/DisposableEffectCompose lifecycle
  • ViewModels created inline (= ViewModel()) instead of hiltViewModel()
  • Large composables (>100 lines) without decomposition
  • Missing keys in LazyColumn items (poor scroll performance)
  • Unstable data classes (non-immutable) causing excessive recomposition
  • Navigation in ViewModel instead of composable → Navigation patterns

Kotlin Issues

  • Nullable types without null checks
  • Mutable collections exposed (MutableList return type)
  • Blocking calls on main thread (runBlocking in UI code)
  • Not using suspend for async operations
  • Ignoring coroutine cancellation (CancellationException caught)
  • GlobalScope usage (leaks) → Use viewModelScope or lifecycleScope
  • !! operator (force unwrap) without justification

Watch For - Performance & Correctness

Coroutines & Flow

  • Collecting Flow without lifecycle awareness (collect vs collectAsState) → Flow best practices
  • Missing flowOn for background work
  • Hot Flow (StateFlow) for one-time events → Use Channel for events
  • Exception handling missing in Flow (catch operator)
  • Not cancelling coroutines properly (leaks)
  • Using GlobalScope instead of structured concurrency

Compose Performance

  • Heavy computation in composition (not in derivedStateOf)
  • Creating new lambdas in composition (not remember)
  • Large lists not using LazyColumn/LazyRow
  • Missing key parameter in LazyColumn items
  • Unnecessary recompositions (unstable classes)
  • Not using @Stable or @Immutable annotations

Room Database

  • Blocking queries on main thread (SELECT not suspended)
  • Missing database migrations
  • Entities with mutable fields
  • @Dao methods not suspend for long operations
  • No indices on frequently queried columns → Room optimization

Memory Leaks

  • Activity/Fragment references in ViewModels
  • Context stored in singletons (use Application context)
  • Listeners not removed in onDestroy
  • Bitmap not recycled after use
  • lateinit properties never initialized (crash risk)

Best Practices - Code Quality

Clean Architecture

  • Domain layer is pure Kotlin (no Android dependencies) → Architecture layers
  • Repository interfaces in domain, implementations in data → Repository pattern
  • Use cases encapsulate business logic → Use Cases
  • Mappers between layers (DTO ↔ Domain ↔ Entity) → Mapper pattern
  • Unidirectional data flow (events up, state down) → UDF

Modern Android

State Management

  • StateFlow for UI state → StateFlow pattern
  • @Published properties in ViewModels
  • Sealed interfaces for screen states (Loading/Success/Error) → State patterns
  • Events via Channel for one-time actions
  • State hoisting in Compose

Security


Quick Scan Table

CategoryWhat to CheckSeverityReference
SecurityNo plain SharedPreferences for secretsBlockEncryptedSharedPreferences
SecurityNo hardcoded API keysBlockSecurity
SecurityCertificate pinning implementedBlockCertificate Pinning
SecurityNo sensitive data in logsBlockSecurity
SecurityProGuard/R8 enabled for releaseBlockObfuscation
ArchitectureNo Android imports in domainBlockClean Architecture
ArchitectureUse cases encapsulate logicFixUse Cases
ArchitectureRepository pattern implementedFixRepositories
ArchitectureNo business logic in ViewModelFixMVVM
DI (Hilt)Constructor injection usedFixHilt
DI (Hilt)@HiltViewModel on ViewModelsFixHilt ViewModels
ComposeSide effects in LaunchedEffectFixCompose Lifecycle
ComposeViewModels via hiltViewModel()FixHilt Integration
ComposeKey parameter in LazyColumnFixPerformance
KotlinNo !! operatorReviewKotlin
KotlinSuspend for async operationsFixCoroutines
KotlinNo GlobalScope usageFixCoroutines
FlowCollect with lifecycle awarenessFixFlow
FlowException handling in FlowFixFlow
RoomQueries are suspendedFixRoom
RoomDatabase migrations definedFixRoom Migrations

Legend:

  • Block PR - Security vulnerability or architecture violation
  • Fix Before Merge - Correctness issue, performance problem, or likely bug
  • Review & Discuss - Code quality improvement

Additional Considerations

Gradle Configuration

  • Minimum SDK appropriate (API 24+ recommended)
  • Target SDK is latest stable
  • Kotlin version consistent across modules
  • Dependencies up-to-date (AndroidX, Compose)
  • Version catalog used for dependency management
  • Build variants configured (debug/release)

Testing

  • Unit tests for ViewModels → Testing
  • Unit tests for Use Cases with fake repositories
  • Compose UI tests for screens
  • Instrumentation tests for Room DAOs
  • TestContainers for API integration tests
  • Turbine for Flow testing
  • MockK for Kotlin mocking

Accessibility

  • Content descriptions on images/icons
  • Semantic properties for Compose components
  • Proper heading hierarchy
  • Touch targets minimum 48dp
  • Color contrast ratio 4.5:1 minimum

Localization

  • All strings in strings.xml (no hardcoded text)
  • Plural resources for counts
  • RTL layout support
  • Date/time formatting with locale
  • Currency formatting appropriate

Block Checklist - Must Fix Before Merge

  • No plain SharedPreferences for secrets (use EncryptedSharedPreferences)
  • No hardcoded API keys or credentials
  • Certificate pinning implemented for production
  • No sensitive data in Logcat
  • ProGuard/R8 enabled for release builds
  • No Android dependencies in domain layer
  • Use cases encapsulate business logic
  • Repository pattern properly implemented
  • Hilt dependency injection used correctly
  • Compose side effects in proper lifecycle hooks
  • No blocking calls on main thread
  • Coroutines properly scoped (no GlobalScope)
  • Room queries are suspended
  • No memory leaks (Context/Activity refs in ViewModels)

Android-Specific Testing Checklist

Before approving Android PRs:

  • Unit tests for ViewModels → Android Testing
  • Unit tests for Use Cases with fake repositories
  • Compose UI tests for critical user paths
  • Room DAO tests (instrumentation or in-memory)
  • Flow tests with Turbine
  • Edge cases tested (null, empty, error states)
  • Coroutine tests with runTest
  • No flaky tests (tests pass consistently)
  • Test coverage meets minimum threshold (80%+)
  • Mutation testing passing (if configured)

Android Framework Guides:

Kotlin Language:

Process: