SKILL.md
ABOUTME: Android/Kotlin, Compose, Clean Architecture, testing, performance
ABOUTME: MVVM + DI conventions, state/side-effect patterns, review checklists
Android/Kotlin
Commands
./gradlew assembleDebug|assembleRelease|test|connectedAndroidTest|lint|ktlintFormat
./gradlew :feature:home:build # Module-specific
See: ASTGREP.md (sg patterns) | _PATTERNS.md | source-control
Version (determine, don't assume)
See ../LANGCOMMON.md. Fetch the truth:
./gradlew --version # Gradle + JVM
grep -E 'kotlin|agp|compose' gradle/libs.versions.toml # version catalog (preferred)
grep -E 'kotlin|android' build.gradle.kts # fallback
cat gradle.properties # AGP / Kotlin flags
curl -s https://api.github.com/repos/JetBrains/kotlin/releases/latest | jq -r .tag_name # latest Kotlin
curl -s https://api.github.com/repos/gradle/gradle/releases/latest | jq -r .tag_name # latest Gradle
Pre-Commit Verification (MANDATORY)
make check && make test-e2e must pass (enforced by the pre-commit-gate hook; see ../LANGCOMMON.md). What make check expands to for Android:
./gradlew ktlintCheck # formatting
./gradlew detekt # static analysis
./gradlew lint # Android lint
./gradlew test # unit tests (all variants)
./gradlew connectedAndroidTest # instrumented / e2e (device/emulator)
Architecture
Clean Architecture Structure
feature/
├── data/repository/, datasource/local/, datasource/remote/
├── domain/model/, repository/ (interface), usecase/
└── presentation/screen/, viewmodel/
Use Cases and ViewModels
Use cases: single responsibility, orchestration here (NOT in ViewModel). ViewModels expose immutable StateFlow UI state and one-time events via Channel/SharedFlow (never StateFlow). For the full SignInUseCase + FeedViewModel + UiState/SideEffect code, see references/compose-patterns.md.
Dependency Injection
| Aspect | Hilt | Koin |
|---|---|---|
| Type | Compile-time | Runtime |
| Build time | Slower | Faster |
| Error detection | Compile | Runtime |
| KMP support | No | Yes |
| Best for | Large/enterprise | Small-medium/KMP |
Hilt: @HiltAndroidApp, @AndroidEntryPoint, @HiltViewModel, @Inject constructor Koin: module { }, single, factory, viewModelOf, koinViewModel()
See references/compose-patterns.md for setup examples.
Compose Essentials
State hoisting: lift state to the caller, pass callbacks down.
State APIs:
remember { mutableStateOf() }, lost on config changerememberSaveable { mutableStateOf() }, survives config changederivedStateOf, computed state
Side effects: LaunchedEffect(key), DisposableEffect. Collect flows with collectAsStateWithLifecycle() (not collectAsState).
Type-safe navigation: @Serializable routes.
See references/compose-patterns.md for detailed examples.
Code Review Checklists
Architecture
- VMs don't chain use cases (orchestration in domain)
- VMs don't call repositories directly
- Use cases have single responsibility
- State immutable (use
copy()) - Side effects use Channel/SharedFlow (not StateFlow)
Compose
- State hoisted appropriately
-
remembervsrememberSaveablecorrect - Side effects use correct APIs
- Stable types for parameters
-
keyused in LazyColumn/LazyRow
Red Flags
| Critical | High |
|---|---|
| Network/DB on main thread | Use case chaining in VM |
| StateFlow for one-time events | Mutable state exposed from VM |
| Hardcoded strings in UI | Missing key in LazyColumn |
| Missing error handling | collectAsState vs collectAsStateWithLifecycle |
| R8 disabled in release |
Detailed References
references/kotlin-features.md- Kotlin language features and idiomsreferences/compose-patterns.md- State, side effects, navigation, image loadingreferences/data-layer.md- Retrofit, Ktor, Room, DataStorereferences/testing-patterns.md- Compose UI tests, ViewModel tests, snapshot tests (Paparazzi), Turbinereferences/performance.md- R8, Baseline Profiles, Compose optimization
Resources
Official: android.com/kotlin | compose | architecture | type-safe nav | baseline profiles
Libraries: Coil | Koin | Hilt | Retrofit | Ktor | Room
Testing: Compose testing | Paparazzi | Turbine