intermediate 2 min answer

Several client applications with different needs consume the same backend. When is a backend-for-frontend justified, and when does it become an unowned distributed monolith?

bffapi-designclientsownershipaggregation
Show the full answer Hide the answer

The problem it addresses

Different clients need different data shapes. A mobile application on a slow network wants one call returning exactly what a screen renders; a web application can afford several calls; a partner integration wants a stable, general-purpose resource model.

Serving all of them from one API produces the familiar compromise: over-fetching for mobile, under-fetching requiring waterfalls, and a generic API that suits nobody — with every client-driven change contending in a single shared surface.

A BFF gives each client class its own thin API layer, aggregating and shaping the underlying services for that client specifically.

When it is justified

  • Genuinely different data shapes and interaction patterns per client, not merely different field subsets.
  • Network constraints that make round trips expensive — mobile being the original motivation, where collapsing five calls into one is a substantial user-visible improvement.
  • Different release cadences. A mobile app has versions in the field for months; a BFF lets the backend evolve while the mobile contract stays stable, which is one of the strongest arguments.
  • The client team owning the BFF. This is the decisive condition — the point is that the team building the experience controls the API serving it, without negotiating with a platform team for every change.
  • A public or partner API with different stability requirements than internal clients.

When it becomes a liability

  • Nobody owns it. A BFF owned by a backend team is just another service in the chain, adding a hop and a deployment without removing any coordination — which is the most common failure and inverts the entire rationale.
  • Business logic accumulates in it. A BFF should aggregate, shape and translate. Once it makes decisions, the same logic exists in several BFFs and they diverge, producing behaviour that differs by client for no intentional reason.
  • Too many BFFs. One per client class is coherent; one per screen is a distributed monolith with a nice name.
  • It becomes the only path, so the underlying services are never usable directly and the BFF is a bottleneck for every change.
  • Duplication across BFFs grows until a shared library appears, which then couples them and reintroduces the coordination the pattern removed.

The alternatives worth weighing

  • GraphQL, which addresses the shape problem generically by letting clients specify what they need. It solves over-fetching well and introduces query complexity, caching difficulty and authorisation-per-field problems — a different set of costs, not fewer.
  • A single well-designed API with sparse field selection and compound endpoints, which is frequently sufficient and is much simpler.
  • Client-side aggregation, acceptable where the network is fast and the client is a browser.

The test

Would the client team be able to ship faster with this layer than without it, and will they own it? If both answers are yes, it is justified. If the second is no, it is an extra hop that will slow everyone down — because the coordination it was supposed to remove has simply moved one service further along.