CMD Guide
HomeSystem DesignMicroservices Patterns

The Problem Traditional Backend Models

A traditional backend exposes one general-purpose API that returns the same response shape to every client, so the payload is inevitably sized for the most demanding consumer — and every leaner client (mobile, watch, TV) then pays to download, decompress, and parse fields it will never render. The Backends-for-Frontends (BFF) pattern exists to break that coupling; to see why it is worth the extra moving parts, you first have to see the cost of the one-size-fits-all API concretely, in bytes and milliseconds.

The mechanism: one contract, many clients

When a single API serves desktop web, an iOS app, and a smart-TV app, its response schema is a union of everything any client might need. A change to satisfy one client (add a field) enlarges the payload for all of them; a change to shrink it for mobile risks breaking desktop. The backend cannot specialise because it has no idea who is calling — it only knows the endpoint. The result is a payload optimised for nobody: too fat for mobile, and often still requiring extra round-trips for desktop.

diagram
diagram

Worked example: over-fetching, field by field

Take an e-commerce catalogue. The mobile browse screen shows a grid of cards; each card renders exactly four fields. The same GET /products endpoint that feeds the rich desktop product page also feeds this grid, so mobile receives the full object:

// what the API returns per product (~3,800 bytes)
{
  "id": 84213, "sku": "HM-AERON-B-GR",
  "name": "Aeron Ergonomic Office Chair",
  "description": "620-char marketing copy...",
  "price": 1395.00, "listPrice": 1595.00, "currency": "USD",
  "thumbnailUrl": "https://cdn.shop.com/p/84213/t.webp",
  "images": [ /* 8 full-res URLs, ~480 B */ ],
  "specs": { /* 12 attributes, ~700 B */ },
  "rating": { "avg":4.7, "count":2831, "latest":[ /*3 reviews, ~900 B*/ ] },
  "inventory": { "warehouses":[ /* ~400 B */ ] },
  "seo": { /* title, metaDescription, canonical, ~300 B */ }
}

// what the mobile card actually uses (~120 bytes)
{ "id":84213, "name":"Aeron Ergonomic Office Chair",
  "thumbnailUrl":"https://cdn.shop.com/p/84213/t.webp", "price":1395.00 }
MetricOne-size-fits-all APIWhat the mobile card needs
Fields per product~404
Bytes per product~3,800~120
20-product page (raw)76 KB2.4 KB
After gzip~19 KB~0.9 KB
Transfer on slow 3G (~50 KB/s)~1.5 s~0.05 s
Over-fetch ratio~31× raw, ~21× gzipped

Note the compression ratios differ: the fat payload compresses ~75% because repeated keys and prose are highly redundant, while the tiny 2.4 KB payload only compresses ~60% — gzip's dictionary and framing overhead are proportionally larger on small bodies. Compression narrows the gap (~31× → ~21×) but never closes it.

The bytes on the wire are only half the cost. The mobile JS engine must still JSON.parse all 20 fat objects — allocating strings for every description, specs, and embedded review it immediately throws away. That deserialisation burns CPU on a battery-powered device and creates garbage-collection pressure that shows up as scroll jank, even though the visible result is four fields per card.

The flip side: chattiness and under-fetching

The same rigid contract that over-serves mobile often under-serves rich screens. A desktop product page that needs the product plus its seller profile plus shipping estimates must call /products/84213, then /sellers/91, then /shipping?zip=… — three sequential round-trips, each paying full latency, because no single endpoint composes exactly that view. So one general-purpose API manages to be simultaneously too fat (mobile) and too chatty (desktop): it is tuned to the endpoint, never to the screen.

Codebase and scaling coupling

Because all client logic lives in one service, client-specific concerns tangle together: a payload tweak for the iOS 3.2 release sits in the same handler as desktop pagination. And you scale at the wrong granularity — a mobile push notification spiking the browse endpoint forces you to scale the entire monolith, including the desktop-only checkout and admin paths that were perfectly healthy.

Pitfalls

Fixing it: BFF vs the named alternatives

The over-fetching/chattiness problem has several standard cures. They are not interchangeable — each trades a different cost.

ApproachHow it trims the payloadYou gainIt costs
BFF (one backend per client type)Server tailors the exact response shape and composes multiple downstream calls per screenOptimal payload & one round-trip per view; client teams own their contract; independent scalingN extra deployables to build, secure, and operate; logic can duplicate across BFFs
GraphQLClient declares the fields it wants; server returns exactly that treeNo new backend per client; solves over- and under-fetching in one round-tripQuery-cost/complexity control, caching (no simple URL cache), and N+1 resolver traps become your problem
Sparse fieldsets (REST ?fields=id,name,price, JSON:API)Client whitelists fields on the existing endpointCheapest change — no new service, no new query languageOnly kills over-fetching, not chattiness; cache keys fragment per field combo; server still composes nothing

How a senior engineer decides

Takeaways


Re-authored and deepened for this guide. Sources: Phil Calçado, "The Back-end for Front-end Pattern (BFF)"; Sam Newman, Building Microservices (2nd ed.), ch. on API gateways & BFF; the Netflix API re-architecture write-ups (Daniel Jacobson) on device-specific APIs; the GraphQL specification (over/under-fetching motivation); and the JSON:API specification on sparse fieldsets. Byte and latency figures are illustrative estimates for a representative catalogue payload.

🤖 Don't fully get this? Learn it with Claude

Stuck on The Problem Traditional Backend Models? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **The Problem Traditional Backend Models** (System Design) and want to truly understand it. Explain The Problem Traditional Backend Models from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **The Problem Traditional Backend Models** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **The Problem Traditional Backend Models** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **The Problem Traditional Backend Models** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes