android-conventions

Defines Android/Kotlin coding conventions for the project. Includes naming rules, forbidden patterns, preferred practices, and code style guidelines. Use when writing code to ensure consistency. Use when user mentions: 네이밍, 컨벤션, 코딩 규칙, 스타일 가이드, 금지 패턴, 권장 패턴, 이름 규칙, 코드 스타일, 컨벤션 확인, 네이밍 규칙.

allowed_tools: Read, Glob, Grep

$ 설치

git clone https://github.com/Enso-Soft/lotto-assist /tmp/lotto-assist && cp -r /tmp/lotto-assist/.claude/skills/android-conventions ~/.claude/skills/lotto-assist

// tip: Run this command in your terminal to install the skill


name: android-conventions description: | Defines Android/Kotlin coding conventions for the project. Includes naming rules, forbidden patterns, preferred practices, and code style guidelines. Use when writing code to ensure consistency. Use when user mentions: 네이밍, 컨벤션, 코딩 규칙, 스타일 가이드, 금지 패턴, 권장 패턴, 이름 규칙, 코드 스타일, 컨벤션 확인, 네이밍 규칙. allowed-tools: Read, Glob, Grep

Android Coding Conventions

프로젝트 코딩 컨벤션 및 스타일 가이드입니다.

Naming Conventions

Classes and Interfaces

TypePatternExample
UseCase{Action}{Subject}UseCaseGetLottoResultUseCase
Repository Interface{Subject}RepositoryLottoRepository
Repository Impl{Subject}RepositoryImplLottoRepositoryImpl
ViewModel{Feature}ViewModelHomeViewModel
Contract{Feature}ContractHomeContract
Screen{Feature}ScreenHomeScreen
Content{Feature}ContentHomeContent
DataSource{Subject}{Type}DataSourceLottoRemoteDataSource
DTO{Subject}DtoLottoDto
Entity{Subject}EntityLottoEntity

Functions

TypePatternExample
UseCase invokeoperator fun invoke()suspend operator fun invoke(round: Int)
Event handleron{Action}onRefresh(), onItemClick()
Private helper{action}{Subject}loadData(), updateState()
Mapperto{Target}()toEntity(), toDomain()

Variables

TypePatternExample
StateFlow_uiState / uiStatePrivate mutable / Public immutable
Channel_effect / effectPrivate / Public
Booleanis{State}, has{Thing}isLoading, hasError
CollectionPlural nounsitems, results, numbers

Modules

feature:{feature-name}    # feature:home, feature:qrscan
core:{core-name}          # core:domain, core:data, core:network

Forbidden Patterns

🚫 절대 사용 금지

PatternReasonAlternative
LiveDataDeprecated patternStateFlow
AsyncTaskDeprecatedCoroutines
GlobalScopeMemory leak riskviewModelScope
runBlocking (production)Blocks threadsuspend + coroutines
findViewByIdOld patternView Binding or Compose
XML layouts (new screens)Migration to ComposeJetpack Compose
Mutable collections (public)Immutability violationImmutable collections

Code Examples

// ❌ Don't
val items = mutableListOf<Item>()  // Public mutable
GlobalScope.launch { ... }
runBlocking { ... }

// ✅ Do
val items: List<Item> get() = _items.toList()  // Immutable copy
viewModelScope.launch { ... }
suspend fun doSomething() { ... }

Preferred Patterns

✅ 권장 패턴

PatternUsage
invoke operatorUseCase entry point
Result<T>Error handling
State hoistingCompose state management
Immutable data classDomain models
suspend functionsOne-shot operations
FlowData streams
Modifier first optionalComposable parameters

Code Examples

// ✅ UseCase with invoke
class GetDataUseCase @Inject constructor(...) {
    suspend operator fun invoke(id: String): Result<Data> = ...
}

// ✅ Result type for errors
suspend fun getData(): Result<Data> = runCatching {
    repository.fetchData()
}

// ✅ Immutable data class
data class LottoResult(
    val round: Int,
    val numbers: List<Int>,  // List is immutable interface
    val bonusNumber: Int
)

// ✅ Modifier as first optional parameter
@Composable
fun MyComponent(
    text: String,                    // Required
    modifier: Modifier = Modifier,   // First optional
    enabled: Boolean = true          // Other optionals
)

Architecture Rules

Layer Dependencies

✅ Allowed:
Presentation → Domain
Data → Domain

❌ Forbidden:
Domain → Data
Domain → Presentation
Presentation → Data (direct)

Module Dependencies

✅ Allowed:
feature:* → core:domain
feature:* → core:di
core:data → core:domain
core:data → core:network
core:data → core:database

❌ Forbidden:
core:domain → core:data
core:domain → core:network
feature:home → feature:qrscan (direct)

Code Style

Imports

// ✅ Explicit imports (preferred)
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.MutableStateFlow

// ⚠️ Star imports (avoid when possible)
import kotlinx.coroutines.flow.*

Formatting

// ✅ Parameter on new line when many
fun createViewModel(
    useCase1: UseCase1,
    useCase2: UseCase2,
    repository: Repository
): ViewModel

// ✅ Chain calls vertically
repository.getData()
    .map { it.toDomain() }
    .catch { emit(emptyList()) }
    .collect { ... }

Comments

// ✅ KDoc for public APIs
/**
 * Fetches lotto result for the given round.
 *
 * @param round The round number to fetch
 * @return Result containing LottoResult or error
 */
suspend fun getLottoResult(round: Int): Result<LottoResult>

// ✅ Explain WHY, not WHAT
// Cache for 5 minutes to reduce API calls during rapid navigation
private val cache = CacheBuilder.newBuilder()
    .expireAfterWrite(5, TimeUnit.MINUTES)
    .build()

// ❌ Don't explain obvious code
// Increment counter by 1
counter++

Korean Market Specifics

Localization Formats

TypeFormatExample
DateYYYY년 MM월 DD일2024년 01월 15일
Time오전/오후 HH:MM오후 08:45
Currency₩{amount:,}₩1,000,000,000

Accessibility

  • Korean TTS pronunciation 고려
  • 문장 길이 ≤ 30자 권장
  • 명확한 동작 설명 ("로또 결과 새로고침")

File Organization

feature/home/
├── HomeScreen.kt           # Screen composable
├── HomeViewModel.kt        # ViewModel
├── HomeContract.kt         # UiState, Event, Effect
├── navigation/
│   └── HomeNavigation.kt   # Navigation setup
└── components/
    ├── LottoResultCard.kt  # Reusable component
    └── NumberBall.kt       # Reusable component