home / skills / hoangnguyen0403 / agent-skills-standard / coroutines

coroutines skill

/skills/kotlin/coroutines

This skill enforces safe structured concurrency in Kotlin by guiding coroutine usage, dispatchers, state and event flows, and proper cancellation.

npx playbooks add skill hoangnguyen0403/agent-skills-standard --skill coroutines

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
1.3 KB
---
name: Kotlin Coroutines Expert
description: Standards for safe, structured concurrency in Kotlin.
metadata:
  labels: [kotlin, concurrency, coroutines, async]
  triggers:
    files: ['**/*.kt']
    keywords: [suspend, CoroutineScope, launch, async, Flow]
---

# Kotlin Coroutines Expert

## **Priority: P0 (CRITICAL)**

**You are a Concurrency Expert.** Prioritize safety and cancellation support.

## Implementation Guidelines

- **Scope**: Use `viewModelScope` (Android) or structured `coroutineScope`.
- **Dispatchers**: Inject dispatchers; never hardcode `Dispatchers.IO`.
- **Flow**: Use `StateFlow` for state, `SharedFlow` for events.
- **Exceptions**: Use `runCatching` or `CoroutineExceptionHandler`.

## Concurrency Checklist (Mandatory)

- [ ] **Cancellation**: Do loops check `isActive` or call `yield()`?
- [ ] **Structured**: No `GlobalScope`? All children joined/awaited?
- [ ] **Context**: Is `Dispatchers.Main` used for UI updates?
- [ ] **Leaks**: Are scopes cancelled in `onCleared` / `onDestroy`?

## Anti-Patterns

- **No GlobalScope**: It leaks. Use structured concurrency.
- **No Async without Await**: Don't `async { ... }` without `await()`.
- **No Blocking**: Never `runBlocking` in prod code (only tests).

## References

[references/advanced-patterns.md](references/advanced-patterns.md)

Overview

This skill codifies safe, structured concurrency practices for Kotlin coroutines. It focuses on cancellation, scope management, dispatcher injection, and correct use of Flow types to prevent leaks and race conditions. The goal is predictable, testable coroutine-based code across Android and server environments.

How this skill works

The skill inspects coroutine usage patterns and enforces rules such as using viewModelScope or scoped coroutineScope instead of GlobalScope, injecting dispatchers rather than hardcoding Dispatchers.IO, and preferring StateFlow for state and SharedFlow for events. It also checks for proper cancellation checks, exception handling, and that async blocks are awaited. Findings map to a mandatory concurrency checklist and recommended anti-pattern removals.

When to use it

  • Reviewing Android ViewModel or lifecycle-aware coroutine usage.
  • Auditing server-side Kotlin coroutine services for leaks and cancellation support.
  • Enforcing team standards during code review or CI linting for coroutine code.
  • Designing new features that require background work, flows, or structured concurrency.
  • Migrating callback or thread-based code to coroutines.

Best practices

  • Always prefer structured scopes (viewModelScope or coroutineScope); avoid GlobalScope entirely.
  • Inject CoroutineDispatchers (e.g., via an abstraction) instead of hardcoding Dispatchers.IO/Main.
  • Use StateFlow for observable state and SharedFlow for one-off events; keep flows cold when appropriate.
  • Check cancellation in long-running loops by testing isActive or calling yield(); ensure scopes are cancelled in lifecycle callbacks.
  • Handle exceptions with runCatching or a CoroutineExceptionHandler and avoid silent failures.
  • Never launch async without awaiting the result; avoid runBlocking in production code.

Example use cases

  • Validating a ViewModel to ensure all launched jobs run in viewModelScope and are cancelled on clear.
  • Checking a repository layer for injected dispatchers and replacing hardcoded Dispatchers.IO usages.
  • Ensuring a long-running background loop respects cancellation via isActive or yield() checks.
  • Converting event emission code to SharedFlow and state holders to StateFlow for consistent UI updates.
  • Flagging use of GlobalScope, runBlocking, or async calls that are never awaited.

FAQ

Why avoid GlobalScope?

GlobalScope creates independent coroutines that outlive structured scopes and cause leaks and uncontrolled work.

When should I inject dispatchers?

Inject dispatchers for testability and flexibility so you can swap execution contexts in tests and avoid platform assumptions.