wandres.dev
CONTAINERS Y WORKFLOWS · lo nuevo en compute

Elegir la herramienta: Workflow, Queue o cron

Cron, Queues y Workflows parecen hacer lo mismo —ejecutar código más tarde— y elegir por ese rasgo superficial es el error clásico. Cada uno desacopla por un eje distinto: cron por el tiempo, Queue por la carga, Workflow por la duración del proceso. Damos el criterio de decisión por la forma del problema, las señales de que elegiste mal, y por qué la madurez consiste en componer las tres en capas —un cron que encola, un consumidor que lanza un Workflow— en lugar de forzar a una a hacer el trabajo de otra.

⏱ 16 min

Cron, Queues y Workflows comparten una superficie engañosa: los tres sirven para “ejecutar código más tarde”. Elegir por ese rasgo —cualquiera vale, total, todos difieren el trabajo— es el error de principiante que produce arquitecturas que se pudren. Cada herramienta desacopla por un eje distinto: el cron responde a “cuándo”, la Queue a “cuántos y en qué orden de llegada”, el Workflow a “qué secuencia, con qué estado, sobreviviendo a qué fallos”. El criterio no está en la herramienta, sino en la forma del problema; y el ingeniero maduro, además de elegir bien, las compone en capas en vez de forzar a una a hacer el papel de otra.

🎯 Al terminar esta lección sabrás
  • Nombrar los tres ejes de desacoplo: tiempo (cron), mensaje (Queue) y proceso (Workflow).
  • Elegir la herramienta por la forma del problema, no por su rasgo superficial.
  • Reconocer las señales de que elegiste mal: un cron o un Queue que reimplementan un Workflow.
  • Componer las tres en capas: un cron que encola, un consumidor que lanza un Workflow.

Tres ejes: tiempo, mensaje, proceso

La confusión nace de lo que comparten; la claridad, de lo que los separa. Un cron desacopla por el tiempo: se dispara según un calendario, ajeno a cualquier petición o mensaje. Su manejador scheduled se enciende, hace su tarea y termina; no guarda estado por elemento ni orquesta nada largo. Es un despertador, no un director de orquesta.

Una Queue desacopla por la carga: un productor deja mensajes y un consumidor los recoge en lotes a su propio ritmo, con reintentos por mensaje, contrapresión cuando el consumidor va lento, y una cola de mensajes muertos para los que envenenan el proceso. Cada mensaje es una unidad independiente; no existe la noción de “paso tres del viaje de este mensaje”.

Un Workflow desacopla por la duración del proceso: una instancia es un único proceso largo, ordenado y con estado, que puede dormir días y reintentar por paso arrastrando su estado hacia adelante. No va de muchas unidades independientes, sino de una unidad que debe avanzar con fiabilidad a lo largo del tiempo.

Cron Triggers

Dirigido por el tiempo. Responde “cuándo”: ejecuta esto cada hora, cada noche, cada lunes. Sin estado por elemento, sin orquestación larga. El manejador scheduled se dispara y acaba.

📨

Queues

Dirigido por el mensaje. Responde “cuántos y en qué orden de llegada”: desacopla productor de consumidor, agrupa en lotes, reintenta por mensaje y aísla los venenosos. Cada mensaje es independiente.

⚙️

Workflows

Dirigido por el proceso. Responde “qué secuencia, con qué estado, ante qué fallos”: un proceso durable, largo y con estado por instancia, con esperas y reintentos por paso.

El criterio de decisión

La decisión se reduce a tres preguntas, formuladas en orden. ¿El disparador es un reloj, algo que debe ocurrir de forma periódica? Entonces es un cron. ¿Es un caudal de unidades independientes que debes absorber a escala sin perder ninguna, desacoplando a quien las produce de quien las procesa? Entonces es una Queue. ¿Es un proceso único que debe avanzar por pasos ordenados, conservar estado, esperar y sobrevivir a fallos? Entonces es un Workflow.

Las señales de que elegiste mal son inconfundibles una vez que sabes mirarlas. Un cron que lee una tabla en cada disparo para averiguar “por dónde va cada cosa” es un Workflow reimplementado a mano. Un consumidor de Queue con un switch gigante sobre un campo de estado es un Workflow reimplementado a mano. Y un Workflow que se lanza por cada mensaje trivial de disparar y olvidar es una Queue sobredimensionada. La herramienta correcta hace que el código deje de pelear consigo mismo.

No compiten, se componen

Lejos de ser rivales, las tres son capas de una misma tubería. Un cron se dispara cada noche y reparte el trabajo encolando un mensaje por cuenta. La Queue absorbe ese pico y entrega cada mensaje a un consumidor con contrapresión y reintentos. Y el consumidor, para los mensajes que exigen un viaje largo, fiable y de varios pasos, arranca una instancia de Workflow por elemento.

export default {
  // 1. cron: dirigido por el tiempo. Se dispara y reparte trabajo
  async scheduled(event: ScheduledEvent, env: Env) {
    const cuentas = await listarCuentasActivas(env);
    await env.COLA.sendBatch(cuentas.map((c) => ({ body: c.id })));
  },

  // 2. Queue: dirigido por el mensaje. Absorbe carga con reintentos
  async queue(batch: MessageBatch<string>, env: Env) {
    for (const msg of batch.messages) {
      // 3. Workflow: dirigido por el proceso. Uno durable por elemento
      await env.FACTURAR.create({ params: { cuentaId: msg.body } });
      msg.ack();
    }
  },
};
flowchart LR
CRON[cron cada noche] -->|encola un mensaje por cuenta| Q[Queue absorbe carga]
Q -->|entrega con reintentos| CON[Consumidor]
CON -->|lanza uno por elemento| WF[Workflow durable]
WF --> OK[proceso completado]
style CRON fill:#cba6f7,color:#11111b
style Q fill:#89b4fa,color:#11111b
style WF fill:#a6e3a1,color:#11111b
Elige por la forma del problema, no por el rasgo de la herramienta

El principiante elige por el rasgo superficial: como los tres “ejecutan código más tarde”, cualquiera parece servir, y toma el que primero recuerda. El ingeniero maduro elige por la forma del problema, y para eso primero identifica qué está preguntando de verdad. El cron responde “cuándo”. La Queue responde “cuántos y en qué orden de llegada”. El Workflow responde “qué secuencia, con qué estado, sobreviviendo a qué fallo”. La unidad más profunda es que los tres desacoplan el instante en que el código se dispara del instante en que se ejecuta, pero lo hacen a lo largo de ejes distintos: el tiempo, la carga y la duración del proceso. Elegir bien es reconocer sobre cuál de esos tres ejes vive realmente tu problema, y esa pregunta casi siempre tiene una sola respuesta honesta. La marca de la madurez, además, es negarse a que una herramienta haga el trabajo de otra: un cron al que le crece una máquina de estados sobre una tabla, un consumidor de cola con un switch monstruoso sobre la etapa, un Workflow invocado para cada mensaje intrascendente. Todos son una herramienta disfrazada de otra, y todos terminan por corromper el sistema con complejidad que no le corresponde. El arquitecto compone en su lugar: un cron que se abre en abanico hacia una Queue que engendra un Workflow por elemento no son tres herramientas compitiendo, sino tres capas de una sola tubería, cada una cargando la preocupación para la que fue diseñada —primero el tiempo, luego la carga, luego el proceso—. Ese encadenamiento es un patrón que reaparece una y otra vez, y reconocerlo es justo lo que convierte un catálogo de productos de Cloudflare en una arquitectura.

⚔️ Clasifica y compón
  1. Toma tres necesidades reales —un informe nocturno, un caudal de webhooks entrantes, un alta de cliente en varios pasos con esperas— y asigna a cada una su herramienta, justificando el eje.
  2. Describe un cron que haya degenerado en una máquina de estados sobre una tabla y explica cómo lo reescribirías como Workflow.
  3. Explica por qué un Workflow por cada mensaje de disparar y olvidar es un desperdicio y qué usarías en su lugar.
  4. Diseña una tubería que encadene las tres capas: cron que reparte, Queue que absorbe, Workflow por elemento. Nombra qué preocupación resuelve cada capa.
  5. Argumenta la frase “los tres desacoplan el disparo de la ejecución, pero por ejes distintos” con un ejemplo propio de cada eje.