Ir al contenido principal
Laravel, thinking fast.

Capitulo 15

Fallar es parte del producto

Julian Beaujardin

Tu función de IA tiene más proveedores detrás de los que crees.

No sólo el modelo. La cosa que guarda lo que generó, la que lo despliega, la que envía el correo al usuario, la que le factura. Cualquiera de ellas puede estar teniendo una mala tarde, y la diferencia entre un producto maduro y uno frágil está enteramente en qué pasa cuando una la tiene.

La versión frágil descubre la caída igual que la descubre un usuario: a mitad de camino, con un 502 en la mano y sin idea de cuál de seis proveedores lo produjo.

Nombra tus dependencias

Empieza por hacer de los proveedores ciudadanos de primera:

/**
 * External services Webplo depends on. Tracked individually so a single vendor
 * incident can be detected and gated without guessing from a raw 502.
 */
enum Dependency: string
{
    case GitHub = 'github';
    case Forge = 'forge';
    case Stripe = 'stripe';
    case OpenAI = 'openai';
    case Fastly = 'fastly';
    case Email = 'email';
    case Registrar = 'registrar';
}

Un enum, no cadenas repartidas por manejadores de error. Ahora «¿está en pie el proveedor del modelo?» es una pregunta cuya respuesta la aplicación puede guardar, en lugar de algo que se deduce de la forma de la última excepción.

Sin esto estás adivinando a partir de un 502. Con esto, la salud es un hecho que registras cuando una llamada falla y consultas antes de hacer la siguiente.

Nombra lo que pierde el usuario

La mitad importante es el segundo enum, porque a un comerciante le da igual qué proveedor está caído:

/**
 * Merchant-facing capabilities, each backed by one or more Dependencys. Flows
 * gate on these and the UI reports on these — always in Webplo's own terms,
 * never the underlying vendor.
 */
enum Capability: string
{
    case CreateSite = 'create_site';
    case Publish = 'publish';
    case ServeSite = 'serve_site';
    case SignUp = 'sign_up';
    case Billing = 'billing';

    public function dependencies(): array
    {
        return match ($this) {
            self::CreateSite => [Dependency::GitHub, Dependency::Forge, Dependency::OpenAI],
            self::Publish => [Dependency::GitHub, Dependency::Forge],
            self::ServeSite => [Dependency::Fastly],
            self::SignUp => [Dependency::Email],
            self::Billing => [Dependency::Stripe],
        };
    }
}

Dos capas, deliberadamente. Las dependencias son nombres de ingeniería; las capacidades son nombres de producto, y el mapa entre ambas es un único match.

Ese mapa vale más de lo que parece. Responde, al instante y sin reunión: el proveedor del modelo está caído, ¿qué pueden seguir haciendo nuestros clientes? Publicar una edición sigue funcionando, porque publicar no necesita el modelo. Servir sitios existentes está intacto. Sólo crear un sitio nuevo está bloqueado. Esa es una respuesta precisa y defendible que, de otro modo, producirían tres personas adivinando en un canal.

Una capacidad está disponible sólo cuando todas las dependencias que necesita están sanas. Crear un sitio necesita tres; cualquiera de ellas lo tumba.