El bug de Kotlin con strings idénticos que no son iguales

Cuando dos strings idénticos no son iguales: el bug de Kotlin que todos deberían conocer

Imagina este escenario: tu sistema de deduplicación está activo, los logs confirman que los IDs son idénticos, y aun así el mismo request pasa dos veces. No hay error en consola. No hay excepción. Solo el sistema comportándose de una manera que parece violar las leyes de la lógica.

Esto es exactamente el desafío que Google for Developers publicó recientemente: un developer en Kotlin intenta prevenir IDs duplicados, todo parece correcto a simple vista, pero la verificación falla silenciosamente. Es el tipo de bug que puede costarte horas —o días— si no sabes dónde buscar.

En este artículo vamos a desmenuzar las causas más probables detrás de este comportamiento, por qué ocurre más seguido de lo que crees, y cómo proteger tu código Kotlin de esta clase de errores en producción.

El problema: igualdad referencial vs. igualdad estructural en Kotlin

Kotlin tiene una distinción muy clara que viene de la JVM: == compara valor (llama a equals()) y === compara referencia (si apuntan al mismo objeto en memoria). En la mayoría de los casos, == es lo correcto para strings. Pero el problema real rara vez está en el operador.

La JVM tiene un mecanismo llamado String Interning: los strings literales que aparecen en el código fuente se almacenan en un pool compartido. Dos variables con el mismo valor literal pueden apuntar al mismo objeto. Pero un string que llega desde una API, una base de datos o un input del usuario es un objeto nuevo en el heap. Si alguien compara con === sin darse cuenta, ahí tienes tu bug.

Sin embargo, hay casos más sutiles que ni siquiera involucran el operador de comparación.

Los culpables menos obvios: Unicode, encodings y caracteres invisibles

Este es el territorio donde los bugs se vuelven realmente traicioneros. Considera estas posibilidades:

  • Caracteres invisibles Unicode: Un string puede contener caracteres de control como el Zero Width Space (U+200B) o el BOM (Byte Order Mark) que son completamente invisibles en los logs pero hacen que dos strings sean diferentes a nivel de bytes.
  • Homoglyphs: La letra latina "a" (U+0061) y la letra cirílica "а" (U+0430) son visualmente idénticas pero son caracteres distintos. Este vector incluso se usa en ataques de phishing.
  • Espacios en blanco al final o al inicio: Un trim() que falta en uno de los dos lados de la comparación es suficiente para que todo falle.
  • Diferencias de encoding: Si un string viene de un sistema con UTF-16 y el otro de UTF-8, la representación interna puede diferir dependiendo de cómo se haga la conversión.

El dato alarmante es que los logs normalmente muestran la representación visual del string, no sus bytes. Entonces puedes tener dos strings que se ven iguales en el log pero que a nivel de memoria son completamente distintos.

¿Cómo aplica esto en tu empresa?

Si tu equipo trabaja con sistemas donde la deduplicación importa —y en casi cualquier sistema de negocio importa— este tipo de bug puede traducirse en consecuencias concretas: pedidos duplicados, pagos procesados dos veces, registros de clientes duplicados en tu ERP, o inconsistencias en tu base de datos que son difíciles de detectar y costosas de corregir.

Hay algunas prácticas que deberían ser estándar en cualquier equipo de desarrollo:

  • Normalizar siempre los strings antes de compararlos: trim(), lowercase(), y si es necesario, normalización Unicode con Normalizer.normalize() de Java.
  • Comparar los bytes reales cuando la igualdad es crítica, no solo la representación visual.
  • Escribir tests específicos con strings que contengan caracteres Unicode no estándar, especialmente si los datos vienen de inputs externos o integraciones con terceros.
  • Revisar los puntos de entrada de datos: APIs, formularios web, archivos CSV importados. Son los lugares donde más frecuentemente se cuelan estos caracteres problemáticos.
  • En sistemas de deduplicación críticos, considera usar un hash determinístico (SHA-256, por ejemplo) sobre el string normalizado, en lugar de comparar strings directamente.

Este desafío de Google es interesante precisamente porque no tiene una única respuesta correcta. Cualquiera de los escenarios anteriores podría ser el culpable, dependiendo del contexto del sistema. Y eso lo hace un excelente ejercicio para que tu equipo discuta y refuerce sus prácticas de code review.

Conclusión

Los bugs más peligrosos no son los que lanzan excepciones. Son los que fallan silenciosamente, dejan pasar datos incorrectos y solo se descubren cuando el daño ya está hecho. La comparación de strings en sistemas de producción es uno de esos casos donde la simplicidad aparente esconde una complejidad real.

Si tu equipo está desarrollando aplicaciones en Kotlin, Android, o cualquier sistema backend en la JVM, vale la pena dedicar una sesión de revisión a cómo están manejando la igualdad de strings en los puntos críticos del sistema.

En Consultoría-Ti trabajamos con equipos de desarrollo en Perú y Latinoamérica para revisar arquitecturas, identificar puntos de falla en sistemas existentes y establecer buenas prácticas de desarrollo. Si quieres que revisemos el código de tu equipo o necesitas apoyo técnico especializado, contáctanos aquí.

Fuentes y Referencias

Google for Developers — Kotlin Developer Challenge (YouTube Shorts)



✨ Contenido generado con ContentFlow — Consultoría-Ti

Compartir
Etiquetas
ckdn: verificación estructurada para agentes de código