A un guard de disparar producción

Un guard me salvó de disparar una Azure Function en producción por accidente. Por qué los guards no dependen de tu memoria, y qué son en la práctica.

Me acabo de salvar de disparar algo en producción sin querer. Y no fue por estar atento — fue porque hace tiempo, sin saber que hoy me iba a salvar, puse un guard.

Hace un tiempo armé una app de consola para administrar Service Bus: ABM completo de queues y topics, receive, peek, send. Desde el día uno le puse guards — uno de escritura (create, update, delete, send) y uno de receive, pensando en "por las dudas".


#El momento

Hoy, mucho después de haber escrito ese código, estaba en una call con un compañero, contándole justo lo que estaba por probar. Quise mandar un mensaje de prueba a una queue. Ejecuté el send. El guard me frenó: escritura bloqueada.

Chequeé a qué entorno estaba apuntando. Producción. Quería testear, y no se me ocurrió verificar el entorno antes de ejecutar.

Lo que hace que esto sea peor de lo que suena: esa queue no es un buzón inerte. Dispara la ejecución de una Azure Function. Si el guard no estaba ahí, no mandaba "un mensaje de prueba" — arrancaba un proceso productivo real, con todo lo que eso implica.


#Uno nunca sabe qué tan abarcativa se vuelve una protección

Cuando puse esos guards, no estaba pensando específicamente "esto también me va a salvar de disparar una Function en producción sin querer". Los puse por una razón más genérica: escritura en Service Bus es peligrosa, punto. La protección terminó cubriendo un escenario mucho más específico y grave de lo que tenía en mente al escribirla.

Cualquiera es Nostradamus con el diario del lunes. Hoy puedo señalar exactamente qué me salvó y por qué. Pero cuando escribí el guard no tenía forma de anticipar este momento puntual — solo la certeza general de que algo, en algún momento, iba a salir mal.

Esa es la única forma honesta de diseñar protecciones: no tratando de predecir el incidente exacto, sino asumiendo que vas a tener uno que no podés predecir.


#¿Qué es un guard, en la práctica?

Nada mágico: un flag booleano que habilita o bloquea la ejecución de un comando o feature. Si está deshabilitado, mostrás un mensaje y cortás ahí. Si está habilitado, seguís.

Es el mismo mecanismo que un Feature Flag — solo que acá el objetivo no es liberar una funcionalidad gradualmente, es frenarte a vos mismo antes de romper algo.

JSON
// appsettings.json
{
  "Guards": {
    "WriteEnabled": false
  }
}
CSHARP
public class GuardOptions
{
    public bool WriteEnabled { get; init; }
}

public class SendMessageCommand(IOptions<GuardOptions> guards)
{
    public async Task ExecuteAsync(string queue, string message)
    {
        if (!guards.Value.WriteEnabled)
        {
            Console.WriteLine("Escritura deshabilitada por guard. Operación cancelada.");
            return;
        }

        // acá sí, el envío real
    }
}

#Dos buenas prácticas, pero una depende de vos y la otra no

Chequear el entorno antes de operar es la práctica obvia. Todos la conocemos. El problema es que depende de que te acuerdes, siempre, sin excepción — y hoy no me acordé.

El guard no depende de mi memoria. Está ahí, bloqueando por default, me acuerde o no de chequear el entorno.

La mejor defensa contra un error futuro no es "prestar más atención". Es diseñar el sistema para que la atención no sea un requisito. Guards por default, no por excepción.

Un yo de hace meses, sin saber que esto iba a pasar, salvó al yo de hoy.

#guards #azure #service-bus #feature-flags