Aprende a integrar código diariamente mientras desarrollas grandes funcionalidades utilizando los patrones de branch by abstraction, feature flags, y connect-last.

Una habilidad fundamental para la integración continua (CI) es la capacidad de realizar cambios en el código que no constituyen funcionalidades completas e integrarlos al tronco (trunk) sin alterar el comportamiento existente. Nunca realizamos cambios drásticos. Hacemos cambios pequeños que limitan el riesgo. Estos son algunos de los métodos más comunes.

Branch by Abstraction#

Sustituir gradualmente el comportamiento existente mientras se integra continuamente:

// Paso 1: Crear abstracción (integrar al tronco)
class PaymentProcessor {
  process(payment) {
    return this.implementation.process(payment)
  }
}

// Paso 2: Agregar la nueva implementación junto con la antigua (integrar al tronco)
class StripePaymentProcessor {
  process(payment) {
    // New Stripe implementation
  }
}

// Paso 3: Cambiar las implementaciones (integrar al tronco)
const processor = useNewStripe ? new StripePaymentProcessor() : new LegacyProcessor()

// Paso 4: Eliminar la implementación anterior (integrar al tronco)

Feature Flags#

Las banderas de funcionalid (feature flags) controlan la visibilidad de las funcionalidades sin bloquear la integración. Sin embargo, a menudo se abusa de ellas; en muchos casos existen mejores alternativas.

Cuándo usar feature flags#

  • Cambios importantes o de alto riesgo que requieren un despliegue gradual (gradual rollout)
  • Pruebas en producción antes del lanzamiento completo (dark launch, pruebas beta)
  • Pruebas A/B y experimentación
  • Comportamiento o interruptores específicos del cliente
  • Coordinación entre equipos que requiere despliegue independiente

Cuándo NO usar feature flags#

  • Nuevas funcionalidades que solo se pueden conectar a pruebas, se integrarán en la confirmación final.
  • Cambios de comportamiento (utilizar Branch by Abstraction en su lugar)
  • Nuevas rutas de API (crea la ruta, exponla como paso final)
  • Corrección de errores (Bug fixes) o parches de emergencia (hotfixes) (desplegar inmediatamente)
  • Cambios sencillos (el despliegue estándar es suficiente)
// Función incompleta integrada al tronco, oculta tras una bandera
if (featureFlags.newCheckout) {
  return renderNewCheckout() // Trabajo en progreso
}
return renderOldCheckout() // Funcionalidad existente estable

// El equipo puede seguir integrando el nuevo código de Checkout diariamente
// La función se mostrará al activar la bandera cuando esté completa

Para obtener orientación detallada sobre la toma de decisiones y los enfoques de implementación, consulte Feature Flags.

Connect Last#

Crea funcionalidades completas y conéctalas en la confirmación final:

// Commits 1-10: Crear nuevos componentes de pago (todos probados e integrados)
function CheckoutStep1() {
  /* probado, funcionando */
}
function CheckoutStep2() {
  /* probado, funcionando */
}
function CheckoutStep3() {
  /* probado, funcionando */
}

// Commit 11: Integración con la interfaz de usuario (integración final)
;<Route path="/checkout" component={CheckoutStep1} />

Por qué importan estos patrones#

Estas prácticas de desarrollo evolutivo permiten a los equipos:

Integración diaria: Divide las funciones grandes en cambios pequeños y seguros. Reducción de riesgos: Cada confirmación se prueba y se puede desplegar. Mantener el flujo: No esperar a que las funciones se completen antes de la integración. Mejora la colaboración: El equipo comparte la propiedad del código en evolución. Habilitar la reversión: Permite deshacer fácilmente pequeños cambios si es necesario.

Preguntas frecuentes#

¿Cómo puedo completar una funcionalidad extensa en menos de un día?#

Probablemente no la completes en un día, pero integrarás el progreso diariamente usando estos patrones. Cada confirmación diaria se prueba, funciona y no afecta la funcionalidad existente.

¿Qué patrón debo usar?#

Connect Last: Ideal para nuevas funciones que no afectan al código existente. Branch by Abstraction:: Ideal para reemplazar o modificar el comportamiento existente. Feature Flags: Ideales para despliegues graduales, pruebas en producción o funcionalidades específicas de un cliente.

“¿Acaso estos patrones no añaden complejidad?”#

Temporalmente, sí. Pero esta complejidad es:

Intencional: Tú controlas cuándo y cómo se introduce. Temporal: Se elimina una vez finalizada la transición. Más segura: Que las ramas de larga duración con conflictos de fusión. Comprobable: Cada paso puede verificarse (probarse) de forma independiente.

Recursos adicionales#

Nota: Este post es una traduccion de Evolutionary Coding Practices bajo la licencia: CC BY 4.0.