Cursor Rules for Kotlin: Coroutines, Modules, and Tests
Kotlin projects need rules about coroutine ownership, Gradle commands, and how multiplatform source sets are divided. Here is a template that covers the most important conventions and the decisions that belong alongside them in fenbs.
Updated 5 min read
Cursor rules for Kotlin tell the AI assistant which coroutine dispatcher to use in which layer, how the Gradle build is structured, and where the multiplatform source boundaries are. Without them, an assistant writing Kotlin code will make plausible choices that compile but violate the threading model or cross source set boundaries in ways that break the build on a different platform.
The architecture decisions behind those boundaries — why coroutine scope is managed at the ViewModel layer, why the data layer is in commonMain rather than platform-specific code — belong in fenbs as Decisions. An assistant that reads both the rules and the project context starts each session with the architectural intent, not just the file structure.
What Kotlin rules should cover
- Coroutine conventions: which dispatcher (
IO,Default,Main) is used in which layer, and who owns theCoroutineScope. - Gradle commands: the exact commands for building, testing, and running the project on each platform.
- Kotlin version and language features: the target Kotlin version and any enabled language features or compiler flags.
- Multiplatform source sets: what goes in
commonMain,androidMain,iosMain, and other targets. - Dependency injection: Hilt, Koin, or manual DI, and where module definitions live.
- Test framework: Kotlin test, JUnit 5, or Kotest, and how to run unit and integration tests separately.
A starting template
# Kotlin project rules
## Build
- Build: ./gradlew build
- Test (all targets): ./gradlew allTests
- Android tests: ./gradlew testDebugUnitTest
- iOS framework: ./gradlew linkDebugFrameworkIosSimulatorArm64 (ask first)
## Kotlin version
- Kotlin 2.0. Do not use deprecated APIs without asking.
## Coroutines
- Use Dispatchers.IO for network and disk operations.
- Use Dispatchers.Default for CPU-bound work.
- Do not create a new CoroutineScope in a function; inject or receive one.
- viewModelScope is for ViewModels only.
## Multiplatform source sets
- Shared business logic: commonMain
- Android-specific: androidMain
- iOS-specific: iosMain
- Do not import platform APIs in commonMain.
## Dependency injection
- Using Koin. Module definitions live in di/ directories.
- Do not add a new module file without asking.
## Tests
- Unit tests use the kotlin.test library.
- Use runTest {} for coroutine tests.Coroutine rules in practice
Coroutine rules are the single most important category for Kotlin projects. An assistant that creates a new CoroutineScope in a repository class instead of injecting one will produce code that cannot be tested or canceled cleanly. An assistant that uses Dispatchers.Main in a data layer will produce code that blocks the UI on some API calls.
Write the coroutine rules as specific as your project needs. If your architecture uses a custom AppScope provided through DI, name it. If certain classes must be called from viewModelScope only, say so. Generic rules about dispatchers are better than none, but project-specific rules are better.
Testing coroutine-based code
Coroutine testing in Kotlin requires specific setup. An assistant that writes tests for coroutine-based code without runTest {} and the test dispatcher will produce tests that pass in isolation but fail under parallel execution or with timing-sensitive assertions. Add the test conventions explicitly so the assistant gets them right from the first attempt.
- Wrap all coroutine tests in
runTest {}from thekotlinx-coroutines-testlibrary. - Use
TestCoroutineDispatcherorUnconfinedTestDispatcherdepending on your coroutine test setup. - Do not use
Thread.sleep()in coroutine tests; useadvanceTimeBy()orrunCurrent()instead. - Flows under test should be collected with
turbineor the standardtoList()in a coroutine scope.
Null safety and type conventions
Kotlin's null safety is one of its strongest features, but it relies on consistent use. An assistant that uses the !! not-null assertion operator instead of proper null handling produces code that crashes at runtime rather than failing to compile. Add a rule: use !! only when a null value would indicate a programming error that should throw an exception, and never in production code paths that handle user input.
For sealed classes and when expressions, add a rule that every when on a sealed class must be exhaustive — either covering all subclasses or including an else branch that logs or throws. An assistant that leaves a when expression non-exhaustive on a sealed class is relying on Kotlin's compiler warning rather than a runtime guarantee.
Architecture decisions and fenbs
Why did the team choose Koin over Hilt? Why is the domain layer in commonMain instead of per-platform? Those decisions shape every new file an assistant creates. Recording them in fenbs as Decisions means an assistant connected to fenbs can answer "should I put this in commonMain or androidMain?" correctly without needing a human to review every placement.
Keep your architecture decisions with the work
Add your Kotlin architecture choices to fenbs as Decisions: the DI framework, the coroutine ownership model, the multiplatform source set strategy. When the architecture changes, record a new Decision with the reason. Every connected assistant reads the current decisions before it creates a new file.
Create a free accountFree for one person · No credit card · Works with Claude, ChatGPT, Cursor and more
How to set up Kotlin rules with fenbs
- Create a free account and add your Kotlin project to fenbs. Record the architecture decisions as Decisions: DI framework choice, coroutine scope ownership, multiplatform strategy.
- Add the template above to your project's
.cursor/rules/directory and fill in your real Gradle commands and Kotlin version. - Connect Cursor to fenbs over MCP from Integrations. Ask the assistant to read the project context at session start before writing any code.
- When the architecture evolves — new platform targets, a different DI approach — update the Decision in fenbs and the rules file together.
What to watch for
iOS-specific Gradle tasks (linking frameworks, running iOS tests) can be time-consuming and require a macOS machine. Add explicit rules requiring the assistant to ask before running any task that targets iOS or runs the native compiler. The same caution applies to any task that touches the Xcode project generated by Kotlin Multiplatform.
Kotlin Multiplatform is still evolving rapidly. If your project uses a newer API that was experimental in an earlier Kotlin version, note the minimum version in the rules file so the assistant does not attempt to use it against a different version.
Data classes in Kotlin are generated with equals, hashCode, and toString implementations based on the properties in the constructor. An assistant that adds a property to a data class will change its equality behavior, which can affect collections, caches, and tests that compare instances. Add a rule: flag changes to data class constructors for review, because they affect equality.