Introducción: por qué el código legacy no es una sentencia
\nCada desarrollador se ha encontrado con código legacy — esa «caja negra» que da miedo tocar porque «funciona y no hay que meterle mano». Sin embargo, cuando la base de código en Kotlin (o Java, que se migra gradualmente) comienza a ralentizar el desarrollo, acumular errores y asustar a los nuevos miembros del equipo, llega el momento de la refactorización. La refactorización de código legacy en Kotlin no es simplemente reescribir, sino un proceso sistemático de mejora de la estructura interna sin cambiar el comportamiento externo.
\nEn este artículo, recorreremos el camino completo: desde el análisis del estado actual hasta la implementación de prácticas modernas de Kotlin. Aprenderá cómo transformar código obsoleto de forma segura, utilizando las capacidades del lenguaje (nullability, extension functions, sealed classes) sin romper el entorno de producción.
\n\n1. Diagnóstico: cómo evaluar la magnitud del código legacy
\nAntes de emprender la refactorización, es necesario entender con qué se está tratando. El código legacy en Kotlin a menudo proviene de proyectos que comenzaron escribiéndose en Java y luego se migraron parcialmente. Los signos típicos son:
\n- \n
- God-objects — clases que hacen de todo: desde análisis sintáctico hasta lógica de negocio. \n
- Falta de pruebas — el código no está cubierto por pruebas unitarias, y cualquier cambio es arriesgado. \n
- Datos mutables — abundancia de
varen lugar deval, listas mutables que se pasan entre módulos. \n - Pesadilla de null — el operador
!!esparcido por todo el código y comprobaciones interminables deif (x != null). \n
Para el diagnóstico, utilice herramientas: Detekt (análisis estático de Kotlin), SonarQube (métricas de calidad) e IntelliJ IDEA Inspections. Cree una lista de verificación de los puntos problemáticos y priorícelos según el riesgo y la frecuencia de uso.
\n\n1.1. Elaboración de un mapa de dependencias
\nTome una hoja de papel o use PlantUML para visualizar las relaciones entre las clases. Si el grafo de dependencias parece una telaraña, es una señal de alerta. Preste especial atención a las dependencias cíclicas: hacen que el código sea frágil y difícil de probar.
\n\n2. Estrategia de refactorización: plan de acción paso a paso
\nLa refactorización sin un plan es un camino hacia el desastre. Utilice el enfoque de «Técnica de pasos pequeños» (baby steps). El algoritmo de acción:
\n- \n
- Documente el comportamiento — escriba pruebas de caracterización (characterization tests) que capturen el comportamiento actual del sistema. \n
- Aísle los cambios — trabaje en una rama separada, use feature toggles para las partes complejas. \n
- Refactorización basada en patrones — aplique técnicas probadas (Extract Method, Replace Conditional with Polymorphism, Introduce Parameter Object). \n
- Migración gradual — no intente reescribir todo de una vez. Use anotaciones
@Deprecatedpara los métodos antiguos. \n
2.1. Ejemplo: sustitución de if-else por when y sealed classes
\nEl código legacy típico en Kotlin contiene cadenas de if-else para manejar estados:
\n// Mal: bandera mutable y if-else\nvar status = "active"\nif (status == "active") {\n // ...\n} else if (status == "inactive") {\n // ...\n} else {\n throw Exception("Unknown status")\n}\nRefactorización con sealed classes:
\n// Bien: modelo type-safe\nsealed class Status {\n object Active : Status()\n object Inactive : Status()\n data class Custom(val reason: String) : Status()\n}\n\nfun handleStatus(status: Status) = when (status) {\n is Status.Active -> // procesamiento\n is Status.Inactive -> // procesamiento\n is Status.Custom -> // procesamiento con reason\n}\nEsto elimina la necesidad de else y hace que el código sea extensible.
3. Trabajo seguro con nullable y colecciones
\nEl código legacy a menudo