Qué es y por qué importa
Un feature flag, en español un interruptor, es un sí o no que la app lee mientras funciona. Si está encendido, el usuario ve la función nueva. Si está apagado, ve la de antes. El código de los dos caminos ya está dentro de la app: el interruptor solo elige cuál usar.
El problema que resuelve es sencillo. Publicas una función, algo se rompe, y para quitarla tienes que hacer otro cambio, otro deploy, quizá esperar la revisión de la tienda. Con el interruptor cambias un valor y la función desaparece. Luego la arreglas con calma.
Si construyes con Claude Code, importa todavía más: publicas a menudo y no siempre has leído cada línea. Poder apagar lo que acabas de publicar, sin tocar el código, te quita mucha ansiedad el día del lanzamiento.
1. Dónde los guardo
Uso una tabla «flags» en la base de datos que la app ya usa: una fila por interruptor, con nombre, encendido sí o no, porcentaje y lista de usuarios. La app la lee al arrancar y de vez en cuando mientras está abierta. Cambio un valor y la función se enciende o se apaga sin una versión nueva. Quien ya tiene la app abierta recibe el cambio en la siguiente lectura de los flags.
La única condición real es esta: el valor tiene que estar en un sitio que puedas cambiar sin un deploy. Los caminos principales son tres.
- Una tabla en la base de datos. Es lo que hago yo: nada nuevo que aprender, y la puedes cambiar también desde una página tuya.
- Un servicio dedicado a los feature flags o a la configuración remota. Te da un panel ya listo, porcentajes y estadísticas. Para empezar, la tabla basta.
- Una variable de entorno. Vale en el servidor, pero en muchos hostings cambiarla requiere un nuevo deploy o un reinicio: comprueba cómo la aplica tu hosting. En una app móvil normalmente queda fijada en la build.
Lo primero que le pido a Claude Code es la tabla y un único punto del código por el que pasen todas las preguntas sobre los interruptores, en lugar de veinte comprobaciones sueltas. Con Claude Code, todo lo que viene después se monta en pocos minutos.
Crea una tabla flags con nombre, encendido sí o no, porcentaje y lista de usuarios, y una única función que la app use para saber si un interruptor está encendido para el usuario actual.
2. La función nace apagada
Cada función nueva entra en el código ya detrás de su interruptor, apagado.
Pon la función X detrás de un interruptor llamado nueva-x, apagado por defecto, leído de la tabla flags.
Luego publico así. Para los usuarios no cambia nada: el código nuevo está, pero no se ejecuta. Es la primera de las tres reglas: ningún valor de la base de datos puede hacer aparecer una función que no está en la app instalada.
En las apps para iPhone esta es la segunda regla, y Apple la deja clara: las funciones nuevas tienen que estar descritas en las notas para la revisión y ser accesibles para quien revisa, y las ocultas están prohibidas. Si publicas una función apagada, dilo en las notas y explica cómo verla.
3. Encender por tramos
Nunca enciendo para todos de golpe. Primero para mí, luego para una decena de usuarios, luego para todos.
Haz que nueva-x se pueda encender para una lista de usuarios y para un porcentaje, siempre el mismo para el mismo usuario.
La parte «siempre el mismo para el mismo usuario» importa. Si la app lo echa a suertes cada vez que se abre, la misma persona ve la función hoy y mañana no. Normalmente se calcula un número fijo a partir del id del usuario: si el número cae por debajo del porcentaje, la función está encendida. Así, cuando subes el porcentaje, quien ya la tenía la sigue teniendo.
Entre un tramo y otro miro los errores y los mensajes de los usuarios. Si todo está tranquilo, subo.
4. Si la lectura falla, todo sigue apagado
El interruptor ya es un kill switch: si la función da problemas, la apago y vuelve la versión antigua. Pero hay un caso en el que nadie piensa: ¿y si la app no consigue leer los flags? Base de datos lenta, sin conexión, una tabla renombrada por error.
Si la lectura de los flags falla, considera todos los interruptores apagados y muestra la versión antigua.
Así, si la lectura falla, la app muestra sola la versión que ya funcionaba. Encenderlo todo cuando no sabe es la forma más rápida de enseñar a todos una función a medias.
Luego está el retraso: quien tiene la app abierta ve la función hasta que vuelve a leer los flags, y también los servicios dedicados suelen guardar los valores en caché. Cuánto tengo que esperar, lo pregunto.
Dime cuánto tiempo pasa, en el peor de los casos, hasta que un usuario con la app ya abierta ve un interruptor que acabo de apagar, y dónde se decide ese tiempo.
5. La tabla solo la cambio yo, y desde el móvil
Para no tener que abrir la base de datos desde el ordenador, uso una página de admin desde el móvil. Pero antes de la página va la protección: esconder un botón no basta. Quién puede modificar la tabla lo tiene que decidir el servidor; si no, alguien más listo enciende y apaga tus funciones sin pasar por la página. Por eso el prompt hace las dos cosas en este orden.
Protege la tabla flags en el servidor: la app puede leerla, pero solo mi cuenta puede modificarla. Luego crea una página de admin, solo para mí, con un interruptor por cada fila.
6. La limpieza
Un interruptor encendido para todos desde hace semanas, y que va bien, ya no sirve. Si se queda ahí, es código viejo que nadie prueba, y una fila que tarde o temprano alguien apaga por error. Además, con la regla del punto 4, si la lectura falla la app vuelve a la versión antigua incluso en una función que usan todos.
Haz una lista de los interruptores encendidos para todos desde hace más de un mes y quítalos del código, dejando la función encendida.
Antes de publicar, compruebo que la función sigue encendida y quito también la fila de la tabla.
Las tres reglas
- El interruptor solo enciende código que ya está en la versión publicada: la función hay que publicarla apagada, antes.
- Apagar código ya publicado no requiere revisión; pero las tiendas no admiten funciones ocultas que se encienden después sin revisión. Los flags sirven para apagar y para encender por tramos, no para colar a escondidas funciones nuevas.
- Nada secreto en los flags: los lee la app, así que quien quiera los ve.
El nombre también cuenta: «descuento-secreto-socios» ya dice demasiado. Claves y contraseñas se quedan en el servidor.
Errores comunes
- Encender el flag antes de haber publicado la función: no pasa nada, porque el código no está. Primero publica apagado, luego enciende.
- Dar por encendido un flag que no se puede leer: si la lectura falla, el valor correcto es apagado.
- Echarlo a suertes cada vez que se abre: el porcentaje tiene que dar siempre el mismo resultado para el mismo usuario.
- Proteger solo la página de admin: la tabla también tiene que aceptar cambios solo desde tu cuenta.
- No quitar nunca los interruptores viejos: los que llevan más de un mes encendidos para todos hay que sacarlos del código.
Lista de comprobación
- Tabla flags creada, una única función para leerla
- La función nueva está publicada apagada
- Porcentaje estable para el mismo usuario
- Si la lectura falla, todo apagado
- Sé cuánto tarda en apagarse para quien tiene la app abierta
- Solo mi cuenta puede cambiar los flags
- De vez en cuando quito los flags encendidos desde hace más de un mes
Preguntas frecuentes
¿Es lo mismo que volver a la versión anterior?
No. Volver atrás deja toda la app como estaba, y en una app móvil significa otra versión y otra revisión. El interruptor apaga solo esa función, y el resto se queda como está.
¿Funciona también en una app que ya está en la tienda?
Sí, y es donde más sirve, porque cada versión nueva pasa por la revisión y no todos actualizan enseguida. Pero solo vale para el código que ya está dentro de la versión instalada: un flag no puede añadir una función que la app no tiene.