← Blog
engineeringarchitecturebackendperformance

The Async Mindset

18 March 2026

The Async Mindset

Most performance problems aren't caused by slow code. They're caused by doing work at the wrong time — specifically, doing it synchronously when the user is waiting, when it could have happened in the background, before or after the request, without blocking anything.

The fix isn't usually optimization. It's a different model of when work happens.

The synchronous default

Everything starts synchronous because synchronous is easier to reason about. A request comes in. You do the work. You return the result. The code reads top to bottom and the behavior matches the reading order. For simple operations, this is fine.

The trap is that synchronous becomes the default even when it shouldn't be. A user submits a form — you send a confirmation email, generate a PDF, call an external API, update three different records, and then return a response. The user waits for all of it. If any step is slow, the whole thing is slow. If any step fails, the whole thing fails. You've made the response time a function of everything that happens to touch that code path.

The synchronous model conflates two things that don't have to be the same: acknowledging a request and completing the work.

Acknowledgment is not completion

Most of the time, users don't need the work to be done — they need to know it will be done. Those are different things, and confusing them is where most of the unnecessary latency comes from.

An email confirmation doesn't have to be sent before you respond. A report doesn't have to be generated before the user sees "your report is being prepared." An image doesn't have to be processed before the upload is acknowledged. In each case, the user needs a receipt — proof that the system received the request and will handle it. They don't need to wait for the handling to finish.

Once you separate acknowledgment from completion, a large class of operations moves out of the request/response cycle entirely. The request returns fast. The work happens in the background, at whatever pace it needs to, retried if it fails, logged if it errors, without any user sitting at a loading spinner waiting for it.

The queue as a primitive

Background jobs need somewhere to live. A queue is the right abstraction — not because it's technically sophisticated, but because it makes the right things explicit.

A queue gives you retry logic for free: if the job fails, you try again, with backoff, up to a limit. It gives you isolation: a spike in one type of work doesn't slow down another. It gives you visibility: you can see what's pending, what's processing, what's failed. These are properties you'd have to build by hand into a synchronous system, and you usually don't, because you're busy shipping features.

The pattern shows up at every scale. At the small end: a single Node.js process with an in-memory queue, jobs running in the background, nothing blocked. At the large end: dedicated workers, distributed queues, dead-letter handling, replay mechanisms. The underlying idea is the same — work that doesn't need to happen now shouldn't happen now.

When to apply it

Async isn't always the answer. Moving everything to the background creates its own complexity: eventual consistency to reason about, failure states to communicate, job deduplication to handle. The discipline is knowing when the tradeoff is worth it.

The test I use: does the user need the result of this operation before they can do anything else? If yes — it needs to be fast, or the UX needs to be redesigned so they don't block on it. If no — it's a candidate for the background.

Sending a notification: no, background it. Charging a card: yes, synchronous, and make it fast. Generating a weekly summary email: no, schedule it. Validating a form input: yes, the user is waiting for feedback. The rule isn't "async whenever possible" — it's "sync only when necessary."


The async mindset is the habit of asking, at each operation: does the user need this result, or do they need to know this was received? The distinction sounds small. It changes how you design almost everything.