Feature Flags in Background Jobs: Evaluate at Execution, Not Enqueue
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
Build vs buy: what a homegrown feature flag system actually costs
The first flag is a boolean and takes eleven minutes. Then come the admin UI, the caching layer, the audit trail and the second SDK. An honest cost breakdown - including the cases where building your own is still the right call.
Top 10 Feature Flag Management Tools in 2026
Featureflow, LaunchDarkly, Statsig, Unleash, Flagsmith, ConfigCat, GrowthBook, PostHog, Harness FME and DevCycle, compared on targeting, runtime config, governance, SDKs and pricing you can predict. We put ourselves first and say why.
Scheduled Releases with Feature Flags: Launch at 9am Without Anyone Awake
A launch time shouldn't depend on someone clicking a button at 09:00. Put the date in the targeting rule, deploy days early, and let every SDK flip the feature at the same instant — no cron job, no deploy window, no one on standby.