FEATUREFLOW V2AI-native · MCP-enabled ·Only 5 spots left at 50% off:NEXT50claim →
Continuous DeliveryAug 7, 2026

Feature Flags in Background Jobs: Evaluate at Execution, Not Enqueue

J
Jordan Mitchell
Senior Engineer

Your web tier checks flags on every request. Your worker checked once — at boot, three deploys ago.

Background jobs break the assumptions that make flags easy in request handlers. A job is enqueued under one flag state and executes under another — minutes later for a queue consumer, days later for a scheduled retry. A worker process runs for weeks, so anything it decided at startup is stale. And when a rollout goes wrong, flipping the flag off stops new web traffic instantly, but a queue with ten thousand pending jobs keeps replaying the old decision.

There's a subtler failure too: jobs often have no obvious user. Evaluate a percentage rollout against the worker's hostname and every job on that box gets the same variant — your 10% rollout becomes 0% or 100% depending on which pod picked up the work.

Three rules for flags in workers

Evaluate when the job runs, not when it's enqueued. The producer shouldn't bake flag decisions into the payload. Evaluate inside the job handler so every execution — including retries — reflects the current rollout state. Snapshot a decision into the payload only when mid-flight consistency matters, like a multi-step email sequence that must not switch templates halfway through.

Key evaluation on the job's subject, not the worker. Use the user the job is about. Featureflow's bucketing is deterministic per user key, so a customer inside the 10% cohort on the web tier lands in the same cohort when their invoice job runs — one consistent experience across both paths.

Give every consumer a kill switch. Long-lived processes shouldn't wait for the next job to notice a change. The Node SDK emits an updated event when feature configuration changes — use it to pause a consumer without a redeploy.

import Featureflow from 'featureflow-node-sdk';

const featureflow = new Featureflow.Client({
  apiKey: process.env.FEATUREFLOW_SERVER_KEY,
});

async function processJob(job) {
  // Evaluate per execution, keyed on the job's subject
  const user = new Featureflow.UserBuilder(job.customerId)
    .withAttribute('plan', job.plan)
    .build();

  if (featureflow.evaluate('new-invoice-renderer', user).isOn()) {
    return renderInvoiceV2(job);
  }
  return renderInvoiceV1(job);
}

// Kill switch: react to flag changes mid-run, no redeploy
featureflow.on('updated', () => {
  if (featureflow.evaluate('pause-invoice-worker', 'invoice-worker').isOn()) {
    queue.pause();
  } else {
    queue.resume();
  }
});

One more safeguard: workers often boot in environments where the first thing to fail is the network. Pre-register features with failover variants so a consumer that can't reach flag config starts in a known-safe state instead of throwing — the Featureflow docs cover this under pre-registering features.

Flags earn their keep in the parts of your system users never see. Request handlers were the easy case — your queues are where rollout state, bucketing keys, and kill switches actually get tested.

#FeatureFlags#BackgroundJobs#MessageQueues#ContinuousDelivery#NodeJS

Put your workers behind flags

Start free with Featureflow — per-job evaluation, deterministic bucketing, and kill switches for every consumer.

Start Now (Free)

Related Articles