Feature flags en Go: cuando cada línea de código es un riesgo
\n\n¿Alguna vez se ha despertado a las 3 de la madrugada porque un nuevo lanzamiento «mató» la producción? ¿O ha perdido horas revirtiendo cambios porque una nueva funcionalidad rompió la lógica de la anterior? ¿Le suena familiar? Entonces los feature flags (banderas de funciones) son lo que cambiará radicalmente su vida como desarrollador Go.
\n\nLos feature flags no son solo un «if-else» en el código. Son una filosofía de lanzamientos seguros. Son la capacidad de activar y desactivar funcionalidades sin hacer deploy, realizar pruebas A/B y hacer lanzamientos canary. En el mundo de Go, donde la velocidad y la confiabilidad son las principales prioridades, los feature flags se convierten en una herramienta imprescindible.
\n\n¿Qué son los feature flags y por qué se necesitan en Go?
\n\nUn feature flag (también conocido como toggle, bandera de funcionalidad) es un mecanismo que permite activar o desactivar una funcionalidad específica de una aplicación sin cambiar el código ni volver a hacer deploy. En palabras simples: usted escribe código, lo envuelve en una condición, y si la bandera está activa, el código se ejecuta; si no, se omite.
\n\nEn Go, donde la compilación en un binario estático y la arquitectura minimalista son el estándar, los feature flags resuelven varios problemas a la vez:
\n\n- \n
- Reducción del riesgo de lanzamientos — ¿Desplegó una funcionalidad pero algo salió mal? Simplemente desactive la bandera. Sin reversión, sin nuevo deploy. \n
- Pruebas A/B — Mostramos la nueva versión solo al 10% de los usuarios, comparamos métricas. \n
- Canary releases — Activación gradual de una funcionalidad para diferentes grupos de usuarios. \n
- Dark launches — El código ya está en producción, pero oculto. Permite probar integraciones sin afectar a los usuarios. \n
- Gestión de dependencias — Activamos la funcionalidad solo cuando la infraestructura backend está lista. \n
Cómo implementar feature flags en Go: de lo simple a lo complejo
\n\n1. La forma más simple: constantes y variables de entorno
\n\nEmpecemos por lo básico. La opción más primitiva pero funcional es usar constantes globales o variables de entorno. Adecuado para un MVP o cuando no se necesita dinamismo.
\n\npackage main\n\nimport (\n "fmt"\n "os"\n)\n\nconst NewCheckoutFlowEnabled = false\n\nfunc main() {\n envFlag := os.Getenv("FEATURE_NEW_PAYMENT")\n isPaymentEnabled := envFlag == "true"\n\n if isPaymentEnabled {\n fmt.Println("Nueva pasarela de pago activa")\n processNewPayment()\n } else {\n fmt.Println("Usando pasarela de pago antigua")\n processOldPayment()\n }\n}\n\nfunc processNewPayment() {\n fmt.Println("Procesando con Stripe API v3")\n}\n\nfunc processOldPayment() {\n fmt.Println("Procesando con Stripe API v2")\n}\n\nLas desventajas son obvias: no hay gestión dinámica, es necesario reiniciar la aplicación. Pero para escenarios simples, está bien.
\n\n2. Enfoque estructurado: su propio gestor de banderas
\n\nCuando hay más de 5 banderas, es momento de extraer la lógica a un paquete separado. Crearemos un gestor simple de feature flags con soporte de almacenamiento en memoria.
\n\npackage feature\n\nimport (\n "sync"\n "time"\n)\n\ntype Flag struct {\n Name string\n Enabled bool\n UpdatedAt time.Time\n}\n\ntype Manager struct {\n mu sync.RWMutex\n flags map[string]Flag\n}\n\nfunc NewManager() *Manager {\n return &Manager{\n flags: make(map[string]Flag),\n }\n}\n\nfunc (m *Manager) IsEnabled(name string) bool {\n m.mu.RLock()\n defer m.mu.RUnlock()\n flag, exists := m.flags[name]\n return exists && flag.Enabled\n}\n\nfunc (m *Manager) Enable(name string) {\n m.mu.Lock()\n defer m.mu.Unlock()\n m.flags[name] = Flag{\n Name: name,\n Enabled: true,\n UpdatedAt: time.Now(),\n }\n}\n\nfunc (m *Manager) Disable(name string) {\n m.mu.Lock()\n defer m.mu.Unlock()\n m.flags[name] = Flag{\n Name: name,\n Enabled: false,\n UpdatedAt: