Yeda AI Tips · #118

English

Prohíbe GlobalScope.launch en los code reviews

Tu asistente de IA acaba de filtrar una corrutina, y nunca vas a ver el error. GlobalScope.launch compila, corre y se ve inofensivo en un diff — exactamente por eso el código Kotlin generado por IA lo usa tan seguido. Conviértelo en un rechazo automático de review.

Por qué GlobalScope es una fuga por diseño

GlobalScope es un CoroutineScope sin ningún Job detrás. Todo lo que lances ahí vive durante toda la vida de la aplicación y no está atado a nada que pueda cancelarlo. La documentación oficial de Kotlin señala tres modos de falla concretos:

  1. Trabajo sin control — la corrutina sigue corriendo después de que desaparece lo que la inició (una pantalla, un request, un job).
  2. Fugas de recursos — una corrutina que nunca se reanuda ni se cancela retiene su memoria, sockets y file handles para siempre.
  3. Crashes silenciosos — no hay padre que reciba las fallas, así que sin un CoroutineExceptionHandler explícito la excepción aparece de forma específica de la plataforma, o no aparece nunca. Esa es la parte de "nunca vas a ver el error".

La API es tan fácil de usar mal que Kotlin la marca como @DelicateCoroutinesApi: hay que hacer opt-in explícito con @OptIn(DelicateCoroutinesApi::class) para usarla sin warning. Trata esa anotación en un diff como una bandera roja, no como una formalidad.

Qué te da la concurrencia estructurada

Concurrencia estructurada significa que las corrutinas forman un árbol: cada corrutina tiene un padre, el padre espera a sus hijos, y cancelar al padre cancela recursivamente a todos los hijos. De ahí salen dos garantías:

GlobalScope.launch se sale de todo el árbol. Ese es el bug completo.

El checklist de review

Ves en el diffPide en su lugar
GlobalScope.launch { ... }Un scope que pertenece al llamador (viewModelScope, un CoroutineScope inyectado, coroutineScope { })
Trabajo secuencial envuelto en launchUna función suspend normal — no hace falta una corrutina nueva
Trabajo concurrente dentro de una función suspendcoroutineScope { launch { } ; launch { } }
catch (e: CancellationException) sin relanzarlaRelánzala — tragársela rompe la cancelación de toda la rama
GlobalScope hardcodeado en una claseUn CoroutineScope inyectado como parámetro del constructor (la práctica oficial recomendada de Android — además hace la clase testeable)

Esa última fila importa especialmente para código generado por IA: hardcodear GlobalScope suele significar Dispatchers hardcodeados también, y código que ningún test puede controlar.

La trampa de la cancelación

La cancelación en corrutinas es cooperativa y viaja como una CancellationException. Dos formas en que el código generado la rompe en silencio:

Cualquiera de los dos bugs más GlobalScope se potencian: trabajo sin dueño que además se niega a morir.

Nivel avanzado: automatiza la prohibición con un linter

No dependas de que un revisor humano lo detecte. detekt incluye la regla GlobalCoroutineUsage (rule set de coroutines) que marca GlobalScope.launch y GlobalScope.async — viene desactivada por defecto, así que actívala:

# detekt.yml
coroutines:
  GlobalCoroutineUsage:
    active: true

Después pon la misma regla en las instrucciones de proyecto de tu asistente de IA ("nunca uses GlobalScope; lanza en un scope que pertenezca al llamador"), para que el código llegue correcto en lugar de rebotar en review. La única excepción legítima que permite la documentación de Kotlin: un job de fondo que de verdad dura toda la vida de la aplicación, lanzado una sola vez, con un CoroutineExceptionHandler explícito y la anotación @OptIn haciendo visible la decisión.

Recursos

Read this article in English

¿Construyendo una funcionalidad con IA? Yeda AI diseña, audita y entrega sistemas LLM de producción.

Habla con nosotros · Lee el blog