A smartwatch and a desktop dashboard both show your fitness stats — but they have wildly different needs. The watch wants three numbers on a tiny screen over a flaky connection. The dashboard wants rich charts, history, and detail. Force both to use the same API and someone loses: either the watch downloads a payload it can't use, or the dashboard makes ten calls to assemble one screen.
Backends for Frontends says: stop compromising. Give each client its own backend, tailored to exactly what that experience needs.
The problem
The classic approach is one general-purpose API that every client shares — mobile, web, third-party integrators, all of them. It sounds efficient, but it slowly becomes a tar pit. Different clients want different data shapes, different fields, different amounts of detail, so the API grows endless optional parameters and bloated responses trying to please everyone.
Worse, the clients are now coupled through that shared API. A change the web team needs can break the mobile app, including old versions still installed on phones that nobody can force to update. Nobody fully owns it, so it ossifies — every change requires cross-team coordination, and the API ends up fitting everyone equally badly.
Step through it below with three apps on one shop API: a web app, a mobile app and a smartwatch. The watch face needs just two fields. Predict how much it has to download to show them, then watch what a harmless-looking change for the web app does to mobile.
How it works
Instead of one shared backend, you build a separate backend per frontend: a mobile BFF, a web BFF, maybe a partner-API BFF. Each one is a thin service that sits in front of your downstream microservices and shapes everything for its one client. The mobile BFF returns small, trimmed payloads in a single call; the web BFF assembles rich, detailed responses.
Each BFF typically does aggregation — fanning out to several downstream services and stitching the results into exactly the shape its client wants, so the device makes one request instead of ten. Crucially, each BFF is owned by the team that owns its frontend, so they can evolve it freely without stepping on anyone else.
Step through the same three apps below, each with its own BFF. Then marketing introduces a new business rule, and you decide where it should live. Pick either one: you can flip to the other afterwards and compare what happens when the rule changes.
Tailor the experience, not the business logic. A BFF should shape, aggregate and format. The moment a pricing, shipping or eligibility rule gets copied into each BFF, you have several teams maintaining the same rule, and sooner or later one copy lags behind and two apps give the same customer two different answers. Keep the rule in the downstream service that owns it, and let the BFFs pass its result along in their own shape.
Your mobile team wants each order in the list to include a product thumbnail URL, which the catalog service already has but the web app doesn't need. With a BFF per frontend, where does the change go?
When to use it
BFF is a great fit when your clients have genuinely different needs — a mobile app, a web app, and a partner API really do want different things — and when separate teams own those frontends and want to move at their own pace. It's a natural evolution of the API gateway: instead of one gateway for all, you get one purpose-built entry point per experience.
The cost is duplication and proliferation. Each BFF is another service to deploy, monitor, and secure, and common logic can get repeated across them (factor truly shared bits into a library or a downstream service). If all your clients want essentially the same data in the same shape, a single well-designed API is simpler — don't split backends just because you can.
Your web app and your public partner API need the same data in the same shape, and one team owns both. Should each get its own BFF?