A team adds a backend-for-frontend per client - web, iOS, Android, partner. Latency and payload sizes improve immediately. What has been given up, and when does that bill arrive?
Show the full answer Hide the answer
What is gained, quantified
The gain is real and it is measured in round trips. A mobile screen assembling data from six services over a 150 ms round-trip link pays roughly 900 ms if the calls are sequential. One aggregating call collapses that to a single round trip plus the slowest backend, and it lets the response carry exactly the fields the screen renders rather than four times as many.
For a high-latency, bandwidth-constrained client this is frequently the single largest available improvement, and it is why the pattern exists.
What is paid
- N implementations of the same policy. Authorisation checks, rate limits, error mapping, pagination semantics and caching now exist in four places, and they drift. The first production incident from a BFF is almost always a permission check present in three of the four.
- A new deployment unit per client, each with its own pipeline, on-call, dependency upgrades and vulnerability surface.
- An ownership question with no comfortable answer. If the client team owns the BFF, they are now running a server, which is a different discipline. If the backend team owns it, every screen change queues behind another team - which is the coupling the BFF was supposed to remove.
- A tempting place to put business logic. The BFF sits between the client and the domain services, so it is the path of least resistance for pricing rules, entitlement checks and validation. Each one that lands there is logic the other three clients do not have.
When the bill arrives
Not at adoption. At the second or third client, and then at every policy change. With one BFF the pattern is pure benefit. With four, a change to the authorisation model is four changes, four reviews, four releases - and the one that is missed is a security defect rather than a bug.
The other arrival point is a reorganisation. A BFF owned by a client team survives exactly as long as that team does.
How to keep the option to reverse
- Keep the BFF a projection, not a service. The rule: it may reshape, filter and aggregate; it may not decide. A BFF that cannot be deleted and replaced by direct calls plus a slower client has become a domain service.
- Put shared policy in a library or the gateway, not in each BFF, so authorisation and rate limiting have one implementation.
- Generate what you can. A schema-driven BFF where the aggregation is declarative resists the drift that hand-written ones accumulate.
- Revisit at every client. Two BFFs with 80% shared code is a signal that one shared aggregation layer with per-client views was the right shape.
When this is the wrong answer
- One client. A BFF for a single web application is an extra hop with extra deployment, and the aggregation belongs in the same service that serves the page.
- When the real problem is API design. If six calls are needed because the domain APIs are too granular, a BFF hides that rather than fixing it, and the next client pays the same cost again.
- A low-latency well-connected client. The round-trip argument is much weaker on desktop over fibre, and the ownership cost is identical.
- When a query language already solves it. If clients can select the fields they need from a single endpoint, the aggregation problem is largely gone - at the cost of a different set of problems around query cost and caching.