# Kotlin Conventions

## Style guide

Kotlin official coding conventions (kotlinlang.org/docs/coding-conventions.html). In IntelliJ / Android Studio enable via *Settings → Editor → Code Style → Kotlin → Set from… → Kotlin style guide*.

## Naming

| Element | Convention | Example |
|---|---|---|
| Class / object / interface | UpperCamelCase | `DeclarationProcessor` |
| Function / property / parameter | lowerCamelCase | `processDeclarations`, `isReady` |
| Constant (`const val` or top-level immutable) | SCREAMING_SNAKE_CASE | `MAX_COUNT`, `USER_NAME_FIELD` |
| Backing property | leading underscore | `_elementList` (private), `elementList` (public) |
| Package | lowercase, no underscores | `com.example.feature` |
| Test function | backticked sentence | `` `parses empty input` `` |

## Formatting

- Indent: **4 spaces**.
- Opening brace at end of line, closing brace on its own line.
- Lambdas: spaces around braces and arrow — `list.filter { it > 10 }`, `map.forEach { (k, v) -> println("$k=$v") }`.
- Trailing comma in multi-line lists / parameters / `when` arms (since Kotlin 1.4).
- Function expression body when the body is a single expression: `fun double(x: Int) = x * 2`.

## Required practices

- Prefer `val` over `var`. Mutability is opt-in.
- Default and named arguments replace constructor / function overloads.
- Prefer the standard library: `filter`, `map`, `groupBy`, `associateBy`, `chunked`, `windowed` over manual loops.
- String templates: `"Hello, $name"`, never `"Hello, " + name`.
- Extension functions are encouraged. Keep them `internal` or file-private unless they belong to a public API.
- `data class` for value-holders. Do not attach behaviour to them.
- Sealed classes / sealed interfaces for closed hierarchies — exhaustive `when` catches new cases at compile time.
- Null safety: never chain `!!`. Use `?.`, `?:`, `let`, `requireNotNull(x) { "why" }`, `checkNotNull(x)`.
- Use `inline` for higher-order functions that are called frequently and pass lambdas, **not** as a default optimization.
- Use `object` for true singletons and `companion object` only when JVM-visible static members are needed.

## Coroutines

- Pick **one** coroutine framework per module. Do not mix coroutines with `CompletableFuture` chains in the same flow.
- Never `runBlocking { ... }` inside another coroutine.
- Pass `CoroutineScope` explicitly; do not use `GlobalScope`.
- Cancel scopes deterministically on lifecycle teardown.
- Use `withContext(Dispatchers.IO)` only for blocking I/O — most application code stays on the default dispatcher.

## Tests

- Framework: **JUnit 5 + kotlin.test**, or **Kotest**. Match the project.
- Backticked function names are idiomatic for tests: `` fun \`returns empty list when input is empty\`() `` .
- Use `kotlinx-coroutines-test`'s `runTest` for suspend tests.
- Use **MockK** over Mockito for Kotlin (handles `final` classes, coroutines, and extension functions correctly).
- Prefer property-based tests (Kotest's `forAll`) for pure transformations.
- Assertion libraries: `kotest-assertions` (`shouldBe`, `shouldThrow`) or AssertJ.

### Self-mock signals to refuse (rule from `clean-code.md` → Testing discipline)

- `spyk(sut)` followed by `every { sut.someMethod() } returns ...` — the SUT's own method is replaced; the test verifies wiring, not behavior.
- `coEvery { sut.suspendMethod() } returns ...` for the suspend equivalent.
- `mockkObject(SomeSingleton)` / `mockkStatic(...)` when `SomeSingleton` *is* the unit under test.
- Reflection or `internal` visibility hacks (`@VisibleForTesting`, `callPrivateFunc` extensions) used to assert on private helpers — that's the implementation-coupling smell, not just a self-mock.

What's fine: `mockk<Collaborator>()` for injected dependencies, `coEvery {}` on those collaborators, and `verify {}` on outcome boundaries (mailer, gateway, repository write).

## Formatter / linter to run

- **ktlint** and / or **detekt**.
- `./gradlew ktlintFormat detekt` if the plugins are configured.
- Run before commit.
