opencoderefactorslegacyarquitectura

OpenCode para Refactors: Transformaciones Seguras de Código Legacy

Por Binary Core

El código legacy es inevitable en proyectos de larga duración. OpenCode transforma el riesgo de refactorizar en un proceso controlado: Plan Mode para diseñar la transformación, Build Mode para ejecutarla, y pruebas automáticas para verificar que nada se rompió.

Este tutorial asume que dominas el flujo Plan/Build y las técnicas de debugging. Aquí nos enfocamos en refactors seguros.

Principios de Refactorización con OpenCode

1. Siempre en Plan Mode primero

Nunca hagas un refactor complejo directamente en Build Mode. Planifica primero:

[Plan Mode]

Quiero refactorizar @src/services/UserService.ts para:
- Extraer la lógica de validación a una clase separada
- Usar el patrón Result<T, E> en lugar de lanzar excepciones
- Migrar de callbacks a async/await

Mantén la misma API pública para no romper consumidores.

2. Refactors incrementales

Divide refactors grandes en pasos pequeños:

Divide este refactor en 3 pasos independientes:
1. Extraer validación a UserValidator
2. Migrar a async/await
3. Cambiar a Result<T, E>

Cada paso debe dejar el código en estado funcional.

3. Tests antes de refactor

Si no hay tests, pide a OpenCode que los genere primero:

Antes de refactorizar @src/lib/calculator.ts, genera tests
que cubran toda la funcionalidad actual. Usa los ejemplos
de uso que encuentres en el código.

Refactors Comunes

Extracción de Funciones

Código monolítico → funciones modulares:

[Plan Mode]

Este método @src/services/OrderService.ts:processOrder
tiene 200 líneas y hace demasiadas cosas.

Extrae funciones separadas para:
- validateOrder
- calculateTotal
- applyDiscounts
- saveOrder
- sendConfirmation

Mantén el mismo flujo de ejecución y retornos.

OpenCode propondrá:

typescript
// Antes async function processOrder(order: Order): Promise<OrderResult> { // 200 líneas de código mezclado } // Después async function processOrder(order: Order): Promise<OrderResult> { const validated = await validateOrder(order); const total = calculateTotal(validated); const discounted = applyDiscounts(total, order.coupon); const saved = await saveOrder(discounted); await sendConfirmation(saved); return saved; }

Renombrado Masivo

Cambiar nombres de forma consistente:

Renombra todas las ocurrencias de 'client' a 'customer'
en @src/services/ y @src/controllers/.

Asegúrate de:
- Variables
- Nombres de funciones
- Comentarios
- Strings en mensajes de error

OpenCode aplicará el cambio en todos los archivos afectados:

typescript
// Antes const client = await findClient(id); client.name = 'New Name'; // Después const customer = await findCustomer(id); customer.name = 'New Name';

Migración de Patrones

Callbacks → Promesas:

[Plan Mode]

Migra @src/lib/database.ts de callbacks a async/await.

Antes:

db.query('SELECT * FROM users', (err, results) => { if (err) throw err; callback(results); });


Después:

const results = await db.query('SELECT * FROM users'); return results;


Actualiza todos los call sites en @src/services/.

Extracción de Clases

Funciones sueltas → clases cohesivas:

[Plan Mode]

Las funciones en @src/utils/validation.ts están relacionadas.
Extráelas a una clase Validator con métodos estáticos.

Agrupa por dominio:
- UserValidator
- EmailValidator
- PaymentValidator

Refactors de Arquitectura

Introducción de Patrones de Diseño

Singleton:

[Plan Mode]

@src/lib/cache.ts se instancia múltiples veces innecesariamente.
Conviértelo a un singleton usando el patrón adecuado.

Mantén la misma API para no romper código existente.

Factory Pattern:

[Plan Mode]

@src/services/PaymentService.ts tiene switch statements para
diferentes proveedores de pago. Usa Factory Pattern.

Crea:
- PaymentProvider interface
- StripeProvider, PayPalProvider classes
- PaymentProviderFactory

Mantén compatibilidad con código existente.

Separación de Concerns

Controller → Service → Repository:

[Plan Mode]

@src/controllers/UserController.ts tiene lógica de negocio
y acceso a datos mezclados.

Separa en:
- UserController: solo HTTP (req/res)
- UserService: lógica de negocio
- UserRepository: acceso a datos

Mantén los mismos endpoints y respuestas.

Modernización de Dependencias

Actualización de APIs obsoletas

[Plan Mode]

@src/lib/http.ts usa axios 0.x con APIs obsoletas.
Actualiza a axios 1.x:

- Cambia `axios.create()` a la nueva sintaxis
- Reemplaza interceptors obsoletos
- Actualiza manejo de errores

Busca todos los usos de axios en el proyecto.

Migración a nuevas librerías

[Plan Mode]

Reemplaza lodash con funciones nativas de JavaScript
en @src/utils/.

Mapa de migración:
- _.map → Array.prototype.map
- _.filter → Array.prototype.filter
- _.reduce → Array.prototype.reduce
- _.uniq → Set
- _.get → optional chaining

Verifica que el comportamiento sea idéntico.

Refactors de TypeScript

Mejora de Type Safety

[Plan Mode]

@src/types/index.ts tiene muchos `any` types.

Reemplaza con tipos específicos:
- Crea interfaces para objetos
- Usa union types para variantes
- Añade type guards para validación
- Usa branded types para IDs

Mantenga compatibilidad con código existente.

Introducción de Generics

[Plan Mode]

@src/utils/api.ts tiene funciones duplicadas para diferentes
tipos de respuesta. Usa generics para generalizar.

Antes:

async function fetchUser(id: string): Promise<User> async function fetchOrder(id: string): Promise<Order>


Después:

async function fetchById<T>(id: string, endpoint: string): Promise<T>

Refactors de React

Extracción de Componentes

[Plan Mode]

@src/components/Dashboard.tsx es un componente de 500 líneas.

Extrae componentes:
- StatsCard
- RecentActivity
- QuickActions
- NotificationsPanel

Mantén el mismo estado y props flow.

Custom Hooks

[Plan Mode]

La lógica de fetch en @src/components/UserList.tsx
se repite en otros componentes.

Extrae a un custom hook:
- useUsers
- useUsersLoading
- useUsersError

Usa el mismo patrón que @src/hooks/usePosts.ts.

Verificación de Refactors

Tests de Regresión

Después de cada refactor:

[Build Mode]

Ejecuta todos los tests para verificar que el refactor
no rompió nada:
- npm test
- npm run test:e2e

Si algún test falla, analiza y corrige.

Comparación de Comportamiento

Para refactors complejos:

[Plan Mode]

Crea un script de comparación que:
1. Ejecute la versión antigua con datos de prueba
2. Ejecute la versión nueva con los mismos datos
3. Compare los resultados
4. Reporte diferencias

Guarda el script en @scripts/compare-refactor.ts

Refactors de Alto Riesgo

Cambios en Schema de Base de Datos

[Plan Mode]

Necesitamos renombrar la columna 'client_id' a 'customer_id'
en la tabla 'orders'.

Plan:
1. Crear migración que añada 'customer_id'
2. Actualizar código para usar ambas columnas
3. Migrar datos de 'client_id' a 'customer_id'
4. Actualizar código para usar solo 'customer_id'
5. Crear migración que elimine 'client_id'

Cada paso debe ser reversible.

Cambios en APIs Públicas

[Plan Mode]

Vamos a cambiar el formato de respuesta de /api/users.

Plan:
1. Añadir nuevo endpoint /api/v2/users con nuevo formato
2. Marcar /api/users como deprecated
3. Migrar consumidores internos a v2
4. Después de 30 días, eliminar /api/users

Mantener backwards compatibility durante transición.

Workflows de Refactorización

Workflow 1: Safe Small Refactor

1. [Plan Mode] Diseña el refactor
2. Genera tests si no existen
3. [Build Mode] Aplica el refactor
4. Ejecuta tests
5. Si falla: /undo y ajusta plan
6. Si pasa: commit

Workflow 2: Large Multi-File Refactor

1. [Plan Mode] Divide en pasos pequeños
2. Para cada paso:
   a. Plan Mode para ese paso
   b. Build Mode para ejecutar
   c. Tests para verificar
   d. Commit cada paso exitoso
3. Integration test al final

Workflow 3: Legacy Code Refactor

1. [Plan Mode] Genera tests de caracterización
2. Tests deben pasar con código actual
3. [Plan Mode] Diseña refactor incremental
4. Aplica un cambio a la vez
5. Tests deben seguir pasando
6. Si tests fallan: el refactor cambió comportamiento

Errores Comunes en Refactors

ErrorCausaSolución
Tests fallan después del refactorCambio de comportamiento no intencionalUsa tests de caracterización antes
Refactor demasiado grandeDifícil de verificarDivide en pasos más pequeños
Rompe compatibilidadNo se verificaron consumidoresRevisa todos los call sites
Performance degradaAlgoritmo menos eficienteMide performance antes y después
Introduce nuevos bugsLógica mal extraídaRevisa cada extracción individualmente

Métricas de Calidad de Refactor

Antes y después del refactor, pide a OpenCode:

Analiza @src/services/OrderService.ts y reporta:
- Complejidad ciclomática
- Número de líneas
- Acoplamiento entre módulos
- Cohesión de funciones
- Coverage de tests

Compara antes y después del refactor.

Conclusión

Refactorizar con OpenCode requiere disciplina:

  1. Planifica siempre: Usa Plan Mode para refactors no triviales
  2. Incremental: Divide cambios grandes en pasos pequeños
  3. Tests primero: Genera tests si no existen
  4. Verifica: Ejecuta tests después de cada cambio
  5. Commitea por pasos: Cada paso exitoso es un commit

En Binary Core, refactorizamos miles de líneas de código legacy sin incidentes graves. La clave es tratar el refactor como cualquier otra feature: minimizar riesgo, maximizar verificabilidad.

¿Listo para automatizar la calidad? Aprende a generar tests automáticamente con OpenCode.


Serie completa:

  1. Instalación y primeros pasos
  2. Flujo de trabajo Plan/Build
  3. Configuración avanzada
  4. Debugging
  5. Refactors (este artículo)

Binary Core

Equipo Binary Core

← Volver al blog