Technology

Background Jobs and Queues: A Practical Guide for Small SaaS Teams

A request that hangs for eight seconds is not a performance problem, it is a background job you have not built yet. Here is how I actually decide what moves off the request thread, and how I keep it from quietly breaking.

Background Jobs and Queues: A Practical Guide for Small SaaS Teams

Background jobs and queues get talked about like proper platform engineering, something you reach for once you have a team big enough to argue about Kafka in a meeting nobody wanted to be in. That has not matched what I have actually seen. I have wired a job queue into products with a few hundred users because the alternative was a support inbox full of "the page just spins" complaints, or worse, a customer who clicked pay twice because the first click never got a response.

The idea is not complicated. Some work does not need to happen while a user is sat there waiting for a response. Sending an email, generating a PDF, calling a slow third party API, resizing an image, none of it needs to block the request. You accept the request, hand the actual work to something else, and tell the user their thing is on its way. Everyone is happier, including your server.

The Signal That Something Should Be a Background Job

The test I actually use is simple. If the user does not need the result the instant they click the button, it belongs in a queue. Sending a welcome email, generating an invoice, syncing data to a third party system, none of that needs to finish before you return a response. The user needs confirmation that it has started, not proof that it has finished.

A second, equally useful test is whether the work can fail and retry without anyone noticing. A slow API call that might time out is a good background job. A calculation the user is watching update live on screen is not. Once you start applying this test honestly, you usually find more of your request handlers qualify than you expected.

Where Small Teams Get This Wrong

Adopting a Message Broker Before They Need One

I have watched teams spend a sprint standing up Kafka for a queue that processes a few hundred jobs a day. That is not ambition, it is a distraction from the product you are meant to be building. If you already have a relational database, a jobs table with a status column will comfortably carry you through your first tens of thousands of users. Reach for something heavier when the simple version actually starts to hurt, not before.

Forgetting the Job Will Run More Than Once

Almost every queue system guarantees a job runs at least once, not exactly once. A worker can crash halfway through, a message can get redelivered, a deploy can restart mid job. If your job is not safe to run twice, it is only a matter of time before it does, and you end up with the same failure mode I wrote about when covering idempotency keys for small SaaS teams. A retried job that sends a second welcome email is annoying. A retried job that charges a card again is a support disaster.

No Visibility Into What Actually Failed

The most common setup I inherit from small teams is a worker that quietly swallows exceptions and moves on. Nobody notices a job has been failing for three days until a customer emails to ask where their export went. A failed job needs somewhere to land, a dead letter queue, a failed status in the jobs table, anything that is visible rather than silently dropped, along with an alert that actually reaches a human.

How I Actually Build This

Start With What You Already Have

Before reaching for a new piece of infrastructure, check what your stack already gives you for free. On .NET projects I default to Hangfire, which runs happily against the same SQL Server or Postgres database you already have, dashboard included, with no new service to operate. Azure Storage Queues do the same job cheaply if you are already on Azure and do not need the extra features. New infrastructure is a cost, not a badge of seriousness.

Separate Queues for Slow Work and Fast Work

One shared queue for everything sounds simpler until a batch of video encoding jobs sits ahead of a password reset email and a customer is locked out for ten minutes wondering why nothing has arrived. Give slow, resource heavy jobs their own queue and their own workers, so a backlog in one does not starve the other. It is a small change that stops a completely avoidable category of complaint.

Make Every Job Safe to Retry

Before a job goes anywhere near production, I ask what happens if it runs twice in a row with the same input. If the answer involves a duplicate email, a duplicate charge or a duplicate record, the job is not ready, whatever the queue system's retry guarantees claim to offer. Building this in from the start is far cheaper than retrofitting it once a customer has already been charged twice.

Watch the Queue, Not Just Whether the Worker Is Running

A worker process reporting healthy tells you almost nothing useful on its own. What you actually want to know is whether the queue is growing, and how old the oldest unprocessed job is. I alert on both. A worker that is technically running but falling further behind by the hour looks fine on every dashboard until someone asks why their export from yesterday still has not arrived.

My Honest Take

Background jobs are one of those pieces of infrastructure nobody notices when they work and everybody notices when they do not. I would rather spend a day getting the basics right, retries, idempotency, and proper failure visibility, than spend a Friday afternoon manually re running jobs and apologising to customers for a second charge that should never have happened.

If your product is starting to hang on slow API calls, PDF generation or anything else that has no business blocking a request, this is exactly the kind of foundational work I help teams get right through software development consulting. It is the same principle running through The 28 Day Startup, the boring plumbing is what lets you actually sleep once real customers are relying on it.

More from the blog

Technology•7 min read

Idempotency Keys: A Practical Guide for Small SaaS Teams

Read more
Dynamics 365•7 min read

Dynamics 365 Duplicate Detection Rules: The Setup Most Teams Get Wrong

Read more
Politics•8 min read

Business Asset Disposal Relief 2026: What Selling Your Business Now Costs You

Read more