Kotlin style rules
Standing rules for Kotlin an assistant writes, covering null safety, immutability, coroutines with structured concurrency, and data and sealed classes.
When you write or change Kotlin code in this project:
Tooling
- Follow the Kotlin coding conventions and the project's formatter or linter (ktlint, detekt, or the IDE's settings in
.editorconfig). Do not reformat code you are not changing. - Use the Kotlin version, JVM target and libraries already in the build. Do not add a dependency for what the standard library does.
Null safety
- No not-null assertions (the !! operator) in production code. Use
?.,?:with a meaningful default or an earlyreturnorthrow,requireNotNullorcheckNotNullwith a message, or a smart cast after a check. - Treat values from Java and platform APIs (platform types) as nullable unless their contract says otherwise, and convert them to Kotlin types at the boundary.
- Do not use
lateinitto dodge initialisation order. Reserve it for framework-injected fields and test setup.
Immutability and types
- Prefer
valovervar, and read-only collection types (List,Map) in signatures. Return copies or read-only views, never a backing mutable collection. - Use
data classfor values and update them withcopy. Keep data classes free of behaviour that depends on identity. - Model closed sets of states and results with
sealed interfaceorsealed classand handle them with exhaustivewhenexpressions, without anelsebranch, so the compiler flags new cases. - Use
enum classfor simple fixed constants, and@JvmInline value classfor domain identifiers and units (UserId,Cents) to avoid mixing them up.
Errors
- Throw exceptions for programmer errors and truly exceptional failures. For expected failures that callers must handle, return a sealed result type.
- Never swallow exceptions. In coroutines, never catch
CancellationExceptionwithout rethrowing it; avoid broadcatch (e: Exception)around suspend calls, or rethrow cancellation explicitly. PreferrunCatchingonly where cancellation cannot occur.
Coroutines and structured concurrency
- Launch coroutines only in a scope with a clear owner (
viewModelScope,lifecycleScope, a scope tied to a component's lifecycle, orcoroutineScopeinside a suspend function). Never useGlobalScope. - Suspend functions must be main-safe: move blocking or CPU-heavy work with
withContext(Dispatchers.IO)orDispatchers.Defaultinside the function, not at the call site. Inject dispatchers so tests can replace them. - Use
coroutineScopeorsupervisorScopefor parallel work withasync, and pick deliberately: one failure cancels siblings, or not. - Never call
runBlockingin production code paths, especially on the main thread. - Expose streams as
Flow. Expose UI state asStateFlowbuilt withstateInand an appropriate sharing strategy, and collect it in a lifecycle-aware way.
Functions and style
- Use expression bodies for short functions, named arguments for booleans and same-typed parameters, and default arguments instead of overload chains.
- Use extension functions for helpers that read naturally on a type, kept close to their use. Do not add extensions on broad types (
Any,String) for one call site. - Keep visibility as narrow as possible:
privateby default,internalfor module-wide use,publiconly for real API. - Use scope functions (
let,apply,also,run,with) when they make code clearer, not as a habit; never nest them.
Tests
- Use the project's test framework (JUnit 5, kotlin.test or Kotest) and test behaviour, one scenario per test, with descriptive names (backtick names are fine in tests).
- Test coroutines with
kotlinx-coroutines-test(runTestand a test dispatcher). NoThread.sleepor real delays. - Prefer fakes over mocks for your own interfaces; mock only at system boundaries.
details
- kind
- Rule: standing instructions for everything the assistant does
- domain
- Software engineering
- category
- Conventions
- made for
- Mobile engineer, Backend engineer, Software engineer
- risk
- read-only
- version
- v1.0.0 · incubating
- reviewed
- 2026-10-03
- works in
- Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Antigravity, OpenCode, Windsurf, Zed, Continue, AGENTS.md, ChatGPT, claude.ai
use in
npx @hermes-hq/hodios install kotlin-style-rules --target claude-codenpx skills add hermes-hq/hodios-dist --skill kotlin-style-rules -a claude-codeRules are always-on instructions, so they are not in the plugins: add the skill, or paste the text into CLAUDE.md.
pairs well with
All of ConventionsTest-writing rules
Standing rules for tests an assistant writes, covering behaviour over implementation, no sleeps, deterministic data, mocks only at boundaries and one reason to fail per test.
test-writing-rulesJava style rules
Standing rules for Java an assistant writes, covering modern language features, immutability, Optional and null handling, exceptions, restrained streams, records and JUnit 5 tests.
java-style-rulesMobile engineer
Acts as a mobile engineer who designs for flaky networks, battery and memory limits, platform conventions and app-store releases. Use for iOS, Android or cross-platform work.
mobile-engineerBackend engineer
Acts as a backend engineer focused on correct data handling, clear API contracts, explicit failure modes and services that are easy to operate. Use as a builder or reviewer persona for server code.
backend-engineerHTTP API design rules
Rules for HTTP APIs covering resource naming, status codes, problem+json errors, cursor pagination, idempotency keys and versioning. Load when designing or changing HTTP endpoints.
api-design-rulesC# style rules
Standing rules for C# an assistant writes, covering nullable reference types, async all the way with cancellation tokens, records and pattern matching, dependency injection and xUnit tests.
csharp-style-rules