El bug silencioso de Kotlin que pasa el build

El bug silencioso de Kotlin: cuando el compilador dice sí pero tu código dice otra cosa

Hay un tipo de bug que es especialmente traicionero: el que no rompe el build, no lanza una excepción, no genera ninguna alerta visible... pero deja tu lógica de negocio completamente rota. Un equipo de Google publicó recientemente un caso real de este tipo usando Kotlin, y vale la pena analizarlo con calma porque el patrón aparece en casi todos los lenguajes modernos con inferencia de tipos.

La situación fue esta: un desarrollador refactorizó código relacionado con la configuración de tareas. El build fue exitoso. Las pruebas pasaron. Pero al revisar el resultado, la lista no contenía objetos Config como se esperaba, sino un valor Unit. Para cualquier desarrollador Java, Unit es el equivalente de void: es lo que Kotlin retorna cuando una función no produce un valor útil. Que aparezca dentro de una lista de configuraciones es una señal de alarma seria.

En este artículo vamos a explorar por qué ocurre esto, qué cambió en la refactorización, y cómo evitar este tipo de errores silenciosos en proyectos reales.

¿Qué es Unit en Kotlin y por qué puede meterse donde no debe?

En Kotlin, Unit es un tipo real, no simplemente la ausencia de valor. Esto es diferente a Java, donde void literalmente no existe como tipo. En Kotlin puedes tener una función que retorna Unit, y ese Unit puede ser inferido, asignado, e incluso almacenado en colecciones si el compilador lo permite.

El problema ocurre cuando refactorizas una lambda o una función y, sin darte cuenta, cambias su tipo de retorno. Por ejemplo, si tienes una función que originalmente retornaba un objeto Config y la refactorizas para que ejecute una acción en lugar de producir un valor, el compilador infiere Unit como tipo de retorno. Si esa función se usa dentro de un map() o cualquier transformación de colección, el resultado será una lista de Unit en lugar de una lista de Config.

El compilador no se queja porque técnicamente el código es válido. Pero semánticamente es un desastre: tienes una colección que parece contener datos pero en realidad contiene nada útil.

El patrón de error y cómo detectarlo

Este tipo de bug tiene una firma reconocible. Generalmente ocurre cuando se mezcla lógica de transformación con lógica de efecto secundario dentro de funciones de orden superior como map(), flatMap() o forEach().

La diferencia clave es esta: map() espera una función que transforma cada elemento y retorna un nuevo valor. Si le pasas una función que ejecuta una acción sin retornar nada explícito, Kotlin infiere Unit como el valor de retorno de esa lambda, y tu lista se llena de Unit.

Una forma de detectarlo rápidamente es declarar explícitamente el tipo de la colección resultante. Si escribes val configs: List<Config> y el compilador empieza a quejarse, acabas de encontrar el bug antes de que llegue a producción. La inferencia de tipos es una herramienta poderosa, pero en situaciones donde el tipo esperado es crítico para la lógica de negocio, ser explícito es siempre la decisión más segura.

Otro mecanismo de protección son los tests unitarios que validan no solo que el código ejecuta sin errores, sino que el resultado tiene el tipo y la estructura correcta. Un test que verifique que la lista tiene elementos de tipo Config habría capturado este bug inmediatamente.

¿Cómo aplica esto en proyectos de software en Perú y Latinoamérica?

En equipos de desarrollo de la región, este tipo de bug silencioso es especialmente peligroso por una razón práctica: muchos proyectos tienen cobertura de tests limitada y procesos de code review poco formales. El flujo de trabajo típico es: desarrollar, compilar, probar manualmente en el happy path, y desplegar. Si el bug no rompe nada visiblemente en el happy path, se va a producción.

El mismo patrón aparece en TypeScript cuando mezclas funciones que retornan void con transformaciones de arrays. Aparece en Dart/Flutter cuando usas map() con funciones que tienen efectos secundarios. Y aparece en cualquier lenguaje con inferencia de tipos donde el desarrollador asume que el compilador lo va a proteger de todos los errores posibles.

En proyectos de desarrollo móvil con Flutter que trabajamos en Consultoría-Ti, este es un punto de revisión estándar en nuestros code reviews: verificar que las transformaciones de colecciones retornen el tipo explícitamente declarado, especialmente cuando el código fue refactorizado recientemente.

¿Cómo aplica esto en tu empresa?

Si tienes un equipo de desarrollo o estás evaluando la calidad del código que produce tu proveedor de software, aquí hay tres prácticas concretas que reducen significativamente este tipo de bugs silenciosos:

  • Declara tipos explícitamente en colecciones críticas. Cuando el resultado de una transformación alimenta lógica de negocio importante, no dependas de la inferencia. Escribe el tipo esperado y deja que el compilador te proteja.
  • Implementa tests que validen estructura, no solo ejecución. Un test que verifica que el código no lanza excepciones no es suficiente. Verifica que el resultado tiene el tipo correcto y la cantidad correcta de elementos.
  • Establece un checklist de code review para refactorizaciones. Cuando alguien refactoriza código existente, el revisor debe verificar específicamente que los tipos de retorno no cambiaron silenciosamente.

Estas prácticas no requieren herramientas adicionales ni presupuesto extra. Requieren disciplina de equipo y un proceso de revisión más consciente.

Conclusión

El caso de Kotlin publicado por Google es un recordatorio de algo que los desarrolladores experimentados saben pero fácilmente olvidan bajo presión: compilar con éxito no es lo mismo que funcionar correctamente. Los lenguajes modernos con inferencia de tipos son herramientas poderosas, pero esa potencia viene con la responsabilidad de entender qué está infiriendo el compilador en cada situación.

La próxima vez que refactorices código que maneja colecciones o transformaciones de datos, tómate un momento adicional para verificar los tipos resultantes. Ese minuto extra puede ahorrarte horas de debugging en producción.

Si tu equipo trabaja con Kotlin, Flutter/Dart o TypeScript y quieres establecer procesos de calidad de código más sólidos, en Consultoría-Ti podemos ayudarte. Contáctanos en consultoria-ti.com.pe/contactus y conversamos sobre cómo mejorar la calidad y confiabilidad del software de tu empresa.

Fuentes y Referencias

Google for Developers — Kotlin Developer Challenge (YouTube Shorts)



✨ Contenido generado con ContentFlow — Consultoría-Ti

Compartir
Etiquetas
Animaciones IA y accesibilidad: el error WCAG que nadie mide