7 Building Blocks Framework

Services and Workers: The Two Task Blocks

A service is a waiter. A worker is the kitchen. Confuse them and your system falls apart under load.

Two Blocks, One Critical Distinction

Of the 7 building blocks that make up every system, two are responsible for doing work: the Service and the Worker. Everything else stores data. These two process it.

The difference between them is simple to state and easy to get wrong: a service makes the caller wait for a response. A worker does not.

That distinction sounds trivial. It is not. Getting it wrong is one of the most common architectural mistakes in production systems, and it is the mistake AI coding assistants make almost every time.

The Service: Your System's Front Door

A service receives a request and returns a response. The caller is actively waiting. Think of it like a restaurant waiter: you place your order, you sit there, and the waiter brings your food back to your table.

Every API endpoint you have ever called is a service. You send an HTTP request, and you get a response. The key properties:

Here is what a basic service looks like:

@app.route("/api/profile/<user_id>")
def get_profile(user_id):
    profile = cache.get(f"profile:{user_id}")
    if not profile:
        profile = db.query("SELECT * FROM users WHERE id = %s", user_id)
        cache.set(f"profile:{user_id}", profile, ttl=300)
    return jsonify(profile)

The caller asks for a profile. The service checks the cache, falls back to the database if needed, and returns the result. Simple, direct, synchronous.

Services are the right choice when the user is waiting for an answer. "Show me my feed." "What is the current price?" "Is this username available?" These are all service calls.

The Worker: Your System's Back Office

A worker picks up tasks and processes them without anyone waiting on the other end. Think of the kitchen in that restaurant. The kitchen does not interact with diners directly. It receives tickets (from a queue), prepares the food, and puts completed dishes on the pass. The kitchen works at its own pace.

Workers have fundamentally different properties than services:

You do not call a worker. You put a message on a queue, and the worker picks it up when it is ready.

When does a worker make sense? Whenever the user does not need the result right now. Sending a welcome email after signup. Generating a PDF report. Encoding a video into multiple resolutions. Resizing an uploaded photo into six thumbnail sizes. All worker jobs.

The Mistake Everyone Makes

Here is where junior engineers (and AI) get it wrong: they put slow work inside a service.

Imagine you are building a photo-sharing app. A user uploads a photo, and you need to create thumbnails in five different sizes. The naive approach puts the thumbnail generation inside the upload service:

  1. User uploads photo
  2. Service receives photo
  3. Service generates five thumbnails (takes 8 seconds)
  4. Service returns "upload complete"

The user stares at a loading spinner for 8 seconds. Now multiply that by a thousand concurrent users. Your service has a thousand threads all blocked on thumbnail generation. Response times spike. Timeouts start. Users retry, making it worse. The system buckles.

The architectural fix:

  1. User uploads photo
  2. Service saves the original to a file store (fast)
  3. Service drops a message on a queue: "process photo #12345"
  4. Service returns "upload complete" (under 1 second)
  5. Worker picks up the message and generates thumbnails at its own pace

The user sees instant confirmation. The thumbnails appear a few seconds later. If a million people upload at once, the queue absorbs the spike and workers chew through the backlog steadily. The system stays healthy.

Same outcome. Radically different resilience.

Why AI Gets This Wrong

When you ask an AI coding assistant to "build a photo upload endpoint," it will almost always generate a single service that does everything inline. Upload, validate, resize, store, update the database, return the response. All in one synchronous call.

That works perfectly for a demo. It falls apart in production.

AI generates code. It does not generate architecture. It does not think about what happens when 10,000 users hit that endpoint simultaneously. It does not consider that thumbnail generation is slow work that should not block the response. It does not weigh the tradeoff between user-perceived speed and processing completeness.

Those are your decisions to make. And the first decision is always the same: is this a service job or a worker job? Does the caller need the result right now, or can it happen later?

The Decision Framework

When you encounter a new piece of functionality, ask two questions:

Is someone waiting for this result? If yes, it belongs in a service. If no, it belongs in a worker.

What happens if this takes 30 seconds? If the answer is "the user stares at a spinner and gets frustrated," you need to split the work. The fast part goes in the service. The slow part goes in a worker.

Some real examples:

Task Block Why
Return a user's profile Service User is waiting for data
Send a welcome email Worker User does not need to wait for email delivery
Validate a credit card Service User needs to know if payment works before proceeding
Generate a monthly report Worker Nobody is sitting there refreshing for it
Check if a username is taken Service User is typing and waiting for validation
Encode an uploaded video Worker Encoding takes minutes; user should not wait

The Pattern in Production

At scale, most systems have far more workers than services. Instagram has a handful of service types (feed, stories, search, profile) but dozens of worker types (thumbnail generation, content moderation, notification delivery, recommendation updates, analytics aggregation).

This makes sense when you think about it. The service is the narrow front door. It needs to be fast and focused. The workers are the sprawling back office, handling all the heavy lifting that makes the product actually work.

Understanding this pattern is what separates engineers who build demos from engineers who build systems. And it is the first architectural decision you will make in the interactive challenges when you design Instagram and Netflix yourself.

Next up: The 3 Storage Extremes covers the five storage blocks and how to pick the right one.

Test Your Understanding

Try designing Instagram, Netflix, and Uber using the building blocks. 3 challenges, 5 minutes each. No signup required.

Try the Interactive Challenges