Refactorización de código legacy en Go: del caos al orden

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿Por qué el código legacy en Go requiere refactorización?

Go (Golang) es famoso por su simplicidad y rendimiento. Sin embargo, incluso en proyectos en Go, con el tiempo se acumula código legacy: construcciones obsoletas, funciones ilegibles, números "mágicos" y falta de pruebas. Trabajar con dicho código se convierte en una pesadilla: cada cambio es arriesgado y agregar nueva funcionalidad lleva semanas.

La refactorización no es reescribir el proyecto desde cero. Es una reorganización segura de la estructura interna del código sin cambiar su comportamiento externo. En este artículo, analizaremos un plan paso a paso para la refactorización de código legacy en Go, desde el análisis hasta la implementación. Aprenderá cómo convertir un "código espagueti" enredado en un proyecto limpio, mantenible y comprobable.



Etapa 1: Auditoría y mapeo del código legacy

Antes de cambiar el código, debe comprender con qué está tratando. Si se salta esta etapa, corre el riesgo de romper una lógica que nadie recuerda.



1.1. Búsqueda de "banderas rojas"

Comience buscando antipatrones típicos en Go:

  • Funciones God — funciones de más de 100 líneas.
  • Variables globales — alto acoplamiento y dificultad para realizar pruebas.
  • Números/cadenas mágicas — constantes dispersas por el código.
  • Falta de manejo de errores — ignorar el error devuelto.
  • Pánico en lugar de errores — usar panic/recover para la lógica de negocio.
  • Mezcla de capas — consultas SQL dentro de manejadores HTTP.


1.2. Creación de un mapa de dependencias

Utilice herramientas como go callgraph o guru para visualizar las llamadas a funciones. Esto ayudará a identificar:

  • Dependencias cíclicas entre paquetes.
  • Código "muerto" que no se llama desde ningún lugar.
  • Rutas críticas que no se deben tocar sin una cobertura de pruebas previa.


Etapa 2: Creación de una "red de seguridad" de pruebas

La refactorización sin pruebas es un salto sin red de seguridad. Su tarea es escribir pruebas de caracterización que fijen el comportamiento actual del sistema, incluso si parece incorrecto.



2.1. Pruebas a nivel de funciones

Comience con las partes más críticas y aisladas del código. Ejemplo para una función que se planea refactorizar:

// Código legacyfunc CalculatePrice(base float64, discountCode string) float64 {    if discountCode == "SALE10" {        return base * 0.9    }    if discountCode == "VIP20" {        return base * 0.8    }    return base}

// Prueba de caracterizaciónfunc TestCalculatePrice(t *testing.T) { tests := []struct { name string base float64 code string want float64 }{ {"sin descuento", 100.0, "", 100.0}, {"descuento 10%", 100.0, "SALE10", 90.0}, {"descuento 20%", 100.0, "VIP20", 80.0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := CalculatePrice(tt.base, tt.code) if got != tt.want { t.Errorf("CalculatePrice(%v, %v) = %v; want %v", tt.base, tt.code, got, tt.want) } }) }}


2.2. Pruebas de integración mediante Golden Files

Para estructuras complejas (respuestas JSON, generación de informes), utilice la técnica de "archivos dorados". Guarde la salida actual en un archivo y, durante la refactorización, compárela con él.

func TestHandler(t *testing.T) {    // Ejecutamos la solicitud    req := httptest.NewRequest("GET", "/api/data", nil)    rec := httptest.NewRecorder()    myHandler(rec, req)

// Comparamos con el estándar expected, _ := os.ReadFile("testdata/golden.json") if !bytes.Equal(rec.Body.Bytes(), expected) { t.Errorf("Output mismatch") }\

Blogs

Book Recommendations