What Happens When You Type a URL and Press Enter
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →Sending emails, resizing images and generating reports shouldn't happen while the user stares at a spinner. How job queues work, retries and idempotency, scheduled tasks and serverless options.
A user clicks "Sign up," and your server creates the account, sends a welcome email, resizes their avatar, notifies the CRM and logs an analytics event, all before responding. If the email provider is slow that day, so is your signup. If it's down, signups fail.
Most of that work doesn't need to happen while the user waits. That's what background jobs are for.
Instead of doing slow work inside the request:
Request -> save user -> enqueue "welcome_email" -> 201 Created (fast)
|
Worker picks it up -> sends email
Rule of thumb: if the user doesn't need the result to see the next screen, it's a candidate.
Every major ecosystem has a mature queue library:
| Stack | Popular options |
|---|---|
| Node.js | BullMQ (Redis-backed) |
| Python | Celery, RQ, Dramatiq |
| Ruby | Sidekiq |
| PHP / Laravel | Laravel Queues |
| Postgres-based | Libraries that use your existing database as the queue |
Using your existing Postgres database as the queue is a great way to start: no extra infrastructure. Move to Redis or a dedicated broker when volume demands it.
Jobs fail: networks blip, APIs return 503, rate limits hit. A good queue retries automatically, with exponential backoff (wait 10 seconds, then 30, then 2 minutes...) so you don't hammer a struggling service.
After a maximum number of attempts, move the job to a dead-letter queue (or "failed jobs" list) for a human to inspect. Don't retry forever, and don't silently drop it.
Retries mean a job might run more than once: it may have succeeded but crashed before reporting success. Design jobs so running twice is harmless:
async function sendReceipt({ orderId }) {
const order = await db.orders.find(orderId)
if (order.receiptSentAt) return // already done, safe to exit
await email.send(receiptFor(order))
await db.orders.update(orderId, { receiptSentAt: new Date() })
}
Enqueue { orderId: 991 }, not the entire order. The job loads fresh data when it runs, so it never works with stale information, and queue payloads stay small.
Some work runs on a schedule rather than in response to a request: nightly reports, cleaning up expired sessions, sending reminders.
Schedule in UTC, and make sure a job can't overlap with its previous run if it takes longer than expected. (Time zone pitfalls.)
On serverless platforms there's no always-on worker process. Options include your platform's queue or background function features, managed queue services that call your endpoints, and cron triggers. The principles (idempotency, retries, small payloads) stay the same.
Most queue libraries come with a dashboard. Alert when failures spike or the backlog grows steadily. (Monitoring basics.)
If a job's result matters to the user, like an export, show a "processing" state and notify them when it's done, by email, an in-app notification or a real-time update. (Real-time options.)
Work that runs outside the web request, in a separate worker process, so users don't wait for slow tasks like sending emails, processing images or generating reports.
Not necessarily. Many libraries can use your existing Postgres database as a queue. Redis-based queues become worthwhile at higher volumes or when you already run Redis.
Running the job more than once has the same effect as running it once. It's essential because retries can cause a job to execute multiple times.
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →There are dozens of HTTP status codes, but about fifteen cover nearly everything. What each one means, when to return it, and the mix-ups (401 vs 403, 301 vs 302) that confuse everyone.
Read →A, AAAA, CNAME, MX, TXT and NS records in plain English, why DNS changes seem slow, and the troubleshooting commands that tell you what is really going on.
Read →