santimattius/structured-coroutines/kotlin-coroutines-skill
Claude Code and agent skill by santimattius/structured-coroutines
- community
Checking that the SKILL.md file is still in the repository.
Load, when a developer writes, reviews, or debugs Kotlin coroutine code and needs help spotting concurrency bugs — GlobalScope leaks, runBlocking in suspend, missing Flow catch, hardcoded or wrong dispatchers, unhandled timeouts, cancellation and structured-concurrency mistakes — or asks how to make async Kotlin testable.
Install
npx vskill@latest install santimattius/structured-coroutines/kotlin-coroutines-skillSKILL.md
Kotlin Coroutines
Overview
This skill provides expert guidance on Kotlin Coroutines, covering structured concurrency, scopes, Dispatchers (including main-safe suspend and dispatcher injection), cancellation (including withTimeout semantics), exception handling (CoroutineExceptionHandler, launch vs async), Channels, Flow (cold vs hot, collectLatest, SharedFlow configuration, blocking in flow {}), lifecycle-aware collection on Android, and testing (virtual time, setMain/resetMain). Use this skill to help developers write safe, maintainable concurrent code aligned with Kotlin 1.9+/2.0+ conventions and official best practices.
Agent Behavior Contract (Follow These Rules)
- Identify the practice or error from the user's code or question (e.g. GlobalScope,
runBlocking in suspend, swallowing CancellationException) and open the corresponding reference from the Triage table in references/.
- Apply the strict rules below in every response. Do not suggest or leave code that violates
them.
- Respond in the required format: Analysis → Erroneous code → Optimized code → Explanation. If
the user only asks a conceptual question (no code), skip erroneous/optimized snippets and focus on analysis and explanation.
- Do not recommend
GlobalScopein production. Use framework scopes (viewModelScope,
lifecycleScope, rememberCoroutineScope), injected scopes, or local scopes ( coroutineScope { }, withContext { }). If an external scope is required, justify and document it.
- Use
asynconly when a return value is needed; ifawait()is never called, uselaunch.
Preserve structured concurrency: inside suspend functions use coroutineScope { } + async/ launch; do not launch in an external scope from suspend unless work must outlive the flow, and then document it.
- Never use
runBlockinginside suspend functions or coroutine-based code. Avoid ending a suspend
function with coroutineScope { launch { } } as the last line when the intent is fire-and-forget — coroutineScope waits for all children and blocks the caller; use an explicit external scope and document it if the work must truly run in the background beyond the caller's lifetime.
- Use explicit Dispatchers:
Dispatchers.Defaultfor CPU-bound work,Dispatchers.Main/
Main.immediate for UI, withContext(Dispatchers.IO) for blocking I/O. Never perform blocking I/O on Default or Main. Do not use Dispatchers.Unconfined in production unless for a rare, documented case. Make suspend functions main-safe: move blocking work into withContext(Dispatchers.IO) so callers on Main are never blocked. Inject CoroutineDispatcher as a constructor parameter (default to real dispatcher; replace with TestDispatcher in tests).
- Never pass
Job()orSupervisorJob()directly to builders (e.g.launch(Job()) { }). Use
supervisorScope { } or a scope defined with SupervisorJob() for supervisor semantics. When running independent tasks with awaitAll, use supervisorScope instead of coroutineScope so one failure does not cancel sibling deferreds.
- Cancellation handling (apply all):
- Never swallow
CancellationException; rethrow it in catch blocks. - Do not use
CancellationExceptionfor domain errors; use normal exceptions instead. - In long loops and repeating/polling work, add
yield(),ensureActive(), orwhile (isActive)
with delay(interval) so the coroutine responds to cancellation.
- For suspend calls in
finally, usewithContext(NonCancellable) { }. - Do not reuse a scope after
scope.cancel(); usecoroutineContext.job.cancelChildren()to
stop only children while keeping the scope alive.
- Prefer
withTimeoutOrNulloverwithTimeoutto avoid unintentionally cancelling the parent
scope. If using withTimeout, catch TimeoutCancellationException explicitly. Always ensure resources opened inside withTimeout are cleaned up in finally.
- Exception handling:
- Uncaught exceptions in
launchpropagate toCoroutineExceptionHandler; inasync, the
exception is stored in the Deferred and only thrown on await(). Always call await() on async blocks to avoid silently losing exceptions.
- Use
CoroutineExceptionHandlerat scope level forlaunchuncaught exceptions. - Do not wrap
await()/join()intry/catchinside the samecoroutineScope { }as the child:
the failure still cancels the scope. Catch around the whole coroutineScope, inside the child, or use supervisorScope.
- In tests use
kotlinx-coroutines-test:runTest, virtual time,advanceTimeBy,
advanceUntilIdle, and inject TestDispatcher/StandardTestDispatcher; avoid real delay() with runBlocking. Replace Dispatchers.Main using Dispatchers.setMain(TestDispatcher()) in @Before and Dispatchers.resetMain() in @After.
- Prefer
produce { }for channels so they close when the coroutine ends. Do not share
consumeEach across multiple consumers; use for (x in channel) per consumer.
- Flow best practices:
- Keep
flow { }builder non-blocking; useflowOn(Dispatchers.IO)or suspend APIs. - Use
StateFlowfor shared UI state (replays last value); useSharedFlowfor events with
explicit replay, extraBufferCapacity, and onBufferOverflow configuration.
- Use
collectLatestonly when cancelling in-progress work is intentional (e.g. search); use
collect when each item must be processed to completion.
- On Android, collect flows with
repeatOnLifecycle(Lifecycle.State.STARTED)or
flowWithLifecycle to stop collection when the UI goes to background.
- When several practices apply (e.g. GlobalScope + wrong Dispatchers), use each relevant reference
and combine the fixes in one optimized snippet.
Recommended Tools for Analysis
When analyzing Kotlin projects for coroutine issues:
- Project settings
- Use
Readonbuild.gradle.kts/build.gradlefor Kotlin version,kotlinx-coroutines-*
dependencies, and kotlinx-coroutines-test.
- Use
GrepforCoroutineScope,GlobalScope,runBlocking,Dispatchers,
viewModelScope, lifecycleScope to locate usage patterns.
- Scope and lifecycle
- Identify whether code runs on Android (viewModelScope, lifecycleScope), KMP, or plain JVM to
recommend the right scope.
Triage-First Playbook (Topic / Error → Reference)
Each references/ref-*.md file is self-contained (Bad Practice, Recommended, Why, Quick fix). Open the linked file directly — no dependency on repo docs when the skill is installed standalone.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingkotlin-coroutines-skill/SKILL.md
Popularity
- Repository stars
- 122
- Repository forks
- 2
- 7-day trend score
- 24
Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 26 Apr 2026, 6 months ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/santimattius/structured-coroutines/kotlin-coroutines-skill)