Cómo usar feature flags en Ruby on Rails sin instalar gemas

¿Cuántas veces te pasó que sacaste una feature a producción y luego te hubiese gustado desactivarla temporalmente para hacerle ajustes o por algún bug inesperado?¿Cuántas veces quisiste activar una feature que estaba en beta solo para algunos usuarios?¿O poder ir deployando una feature pero que aún no esté disponible para los usuarios? Me animaría a decir que fueron varias.

Las feature flags vienen a darnos la ✨solución mágica✨ a todo lo anterior:

  • Permiten habilitar y deshabilitar una feature fácilmente
  • Permiten lanzar algo a público de forma gradual, solo para algunos usuarios (por ubicación, por rol, etc)
  • Permite ir mergeando a main sin necesidad de acumular en otra rama (con el posterior esfuerzo que implica hacer el merge de algo muy grande)
  • Permiten activar features por entorno

Las feature flags son esencialmente variables condicionales que determinan si una feature debe estar activa o inactiva.

Hay diversas formas de implementar feature flags en Ruby en Rails, pero acá te voy a contar una que nos resulta muy práctica a nosotros:

  1. Creamos un initializer (/config/initializers/feature_flags.rb) en donde se define una constante por cada feature flag, con la condición necesaria para habilitar el módulo.
# frozen_string_literal: true

FEATURE_FLAGS = {
feature_flag_a: ENV['ENV_VAR_A'] == 'true',
feature_flag_b: ENV['ENV_VAR_B'] == 'true',
feature_flag_c: Rails.env.production?,
feature_flag_d: !Rails.env.development?,
}.freeze

Dato de color
Dependiendo el caso, solemos guardar en variables de entorno un booleano para determinar si habilitamos o no un módulo (casos feature_flag_a y feature_flag_b) o consultamos directamente por el entorno en sí (casos feature_flag_c y feature_flag_d).

2. Creamos una clase en services (/app/services/feature_flags.rb) con un método de clase que se encargará de devolvernos el valor de la feature flag recibida por parámetro:

# frozen_string_literal: true

class FeatureFlag
def self.enabled?(feature_flag_name)
FEATURE_FLAGS[feature_flag_name.to_sym]
end
end

3. Listo! Ya podés usar dentro de tu código cualquier feature flag definida y todas las veces que quieras:

def method_a
if FeatureFlag.enabled?(:feature_flag_a)
...
end
end
def method_b
return unless FeatureFlag.enabled?(:feature_flag_b)

...
end

El uso de feature flags conlleva una implementación muy básica pero muy poderosa. Según las necesidades de cada proyecto podrían complejizarse tanto como uno quiera. En Unagi usamos esta implementación súper básica en más de un proyecto y la verdad que nos alcanza y sobra.

¿Qué es lo mejor de todo? No necesitás instalar ninguna gema extra a tu proyecto y el código te queda súper legible y prolijo 🎉.

¿Te animás a meterle on-off a las features de tu proyecto?