OpenCode para Refactors: Transformaciones Seguras de Código Legacy
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
| Error | Causa | Solución |
|---|---|---|
| Tests fallan después del refactor | Cambio de comportamiento no intencional | Usa tests de caracterización antes |
| Refactor demasiado grande | Difícil de verificar | Divide en pasos más pequeños |
| Rompe compatibilidad | No se verificaron consumidores | Revisa todos los call sites |
| Performance degrada | Algoritmo menos eficiente | Mide performance antes y después |
| Introduce nuevos bugs | Lógica mal extraída | Revisa 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:
- Planifica siempre: Usa Plan Mode para refactors no triviales
- Incremental: Divide cambios grandes en pasos pequeños
- Tests primero: Genera tests si no existen
- Verifica: Ejecuta tests después de cada cambio
- 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:
- Instalación y primeros pasos
- Flujo de trabajo Plan/Build
- Configuración avanzada
- Debugging
- Refactors (este artículo)
Binary Core
Equipo Binary Core