Good Code
The good version returns a transformed Flow, leaving collection, cancellation, and error reporting to the caller's lifecycle.
Lesson 07
Keep Flow pipelines cancellable and let callers choose where collection starts.
fun approvalMessages(events: Flow<ReviewEvent>): Flow<String> {
// The Flow pipeline leaves collection and cancellation with the caller.
return events
.filterIsInstance<ReviewApproved>()
.map { event -> event.title }
}val messages = mutableListOf<String>()
fun start(events: Flow<ReviewEvent>) {
GlobalScope.launch {
// Background collection outlives the screen or request that started it.
events.collect { event -> messages += event.toString() }
}
}The good version returns a transformed Flow, leaving collection, cancellation, and error reporting to the caller's lifecycle.
The bad version starts collection inside a helper and stores messages in shared mutable state, so shutdown and failure paths are hard to trace.