100%

Lesson 2 of 2

Duplication, Ownership, and the Alternatives

BFFs trade duplication for autonomy. The trade is good under specific conditions and poor outside them — and there are two serious alternatives worth evaluating first.

10 min read

Every BFF is another deployable to build, run, secure and monitor, and each duplicates some aggregation its siblings also perform. That trade buys team autonomy and per-client optimisation. Whether it is worth making depends on conditions you can check.

ConditionWhy it mattersIf absent
Separate teams own separate clientsAutonomy is the main benefit; one team owning all clients gains littleThe BFFs become three codebases one team maintains
Client needs genuinely conflictIf they do not, one API serves both perfectly wellDuplication with no compensating benefit
Each team can operate a backendOwning a BFF means deploying, monitoring and being on call for itUnowned services that degrade quietly
Business logic is genuinely elsewhereBFFs must stay thin, or clients diverge behaviourallyTwo implementations of the same rule, disagreeing
The conditions that make the trade good. Missing several of these means the pattern will cost more than it returns.

Three ways to serve several clients

A BFF per experience

Each client team owns a backend that aggregates and shapes for its client only.

  • Teams ship without queueing behind another team
  • Per-client payload and round-trip optimisation
  • A client-specific change cannot affect other clients
  • A deployable per client, with its own operational surface
  • Duplicated aggregation across BFFs
  • Business logic tends to migrate in unless actively policed

Choose when: Separate teams, genuinely conflicting client needs, and the operational capacity to run one backend per team.

A query language such as GraphQL

One endpoint where each client requests exactly the fields it needs, resolved against the services behind it.

  • Clients shape their own responses without a per-client backend
  • One place to operate and secure
  • Adding a field serves every client at once
  • Query cost control, depth limiting and caching are genuinely hard
  • Authorisation must be enforced per field, not per endpoint
  • The schema becomes a shared component teams queue behind

Choose when: Client needs differ mainly in *which fields* they want rather than in aggregation logic, round-trip budget or release cadence.

Keeping BFFs healthy over time

  1. 1

    Write down what a BFF may and may not do

    May: aggregate, reshape, filter, adapt formats, apply client-specific caching. May not: decide eligibility, compute prices, own data, or make writes the domain services do not expose as operations.

  2. 2

    Keep authentication at the edge, authorisation in the services

    The gateway establishes who the caller is; the domain services decide what they may do. A BFF that makes authorisation decisions becomes a second, weaker policy implementation per client.

  3. 3

    Review the BFFs together, quarterly

    Compare what each is doing. Similar aggregation is expected; a rule appearing in two of them is a defect to move down into a service before the two implementations drift.

  4. 4

    Let a BFF die when its client does

    One of the genuine benefits: retiring a client means deleting its BFF entirely. That only works if nothing else ever started calling it, which is worth checking rather than assuming.

  5. 5

    Measure whether the queue actually went away

    The justification was team autonomy. If client teams are still blocked — now on the services team instead — the boundary was drawn in the wrong place and the BFFs are cost without benefit.

In practice

Retiring a BFF, and finding out who else used it

A company sunset its Android app after moving to a cross-platform client. The Android BFF was scheduled for deletion, which was supposed to be a one-day task.

Constraints

  • The BFF had existed for three years
  • No documented consumers other than the Android app
  • Deletion scheduled as part of the app sunset
  • Access logs retained for 30 days

Decision

Check the access logs before deleting. They showed traffic from an internal reporting tool, a partner's integration and a monitoring script — none of which anyone remembered. Deletion was rescheduled, consumers were migrated over six weeks, and the endpoints were made to return an explicit deprecation header first.

Why

A BFF is meant to have exactly one consumer, and that property must be enforced rather than assumed. Three consumers had arrived over three years precisely because the endpoints were convenient and nothing prevented their use. The logs cost an hour to check and prevented an outage in a partner integration.

What it cost

Six weeks instead of one day, and a permanent change to how they operate BFFs: each now authenticates its intended client specifically, so an unintended consumer fails immediately rather than succeeding for three years. That is slightly more setup per BFF and it preserves the disposability that made the pattern attractive in the first place.

Two BFFs both need a new "customer tier" calculation

As a developer

Implements it in both, since each team owns its own BFF and coordinating would mean waiting for the other team. The calculation is short and the duplication feels acceptable.

As an architect

Reads it as a rule that escaped the domain. Customer tier is a business concept with one correct answer, so two implementations will disagree — the classic must-change-together case from Chapter 2. The right home is a domain service that both BFFs call, and the fact that it surfaced in two BFFs at once is useful evidence that it was missing from the services layer, not a reason to write it twice.

Your web and mobile BFFs share about 30% of their aggregation code. Someone proposes a shared library. What is your view?Reveal

It depends on what the shared 30% is, and the two categories deserve opposite answers. Technical mechanics with one reason to change — response envelopes, error mapping, pagination helpers, tracing propagation — are genuinely shared knowledge and belong in a library. Aggregation that shapes data for a screen is not: the two BFFs do it similarly today because the two screens happen to be similar, and the moment one client redesigns, the shared function acquires a parameter, then another, and both teams are back to coordinating changes — which is precisely the queue the BFFs were created to remove. Apply the must-change-together test per piece rather than to the 30% as a block, and be aware that a shared BFF library tends to grow, so it is worth reviewing what it contains once a quarter.

Key takeaways

  • BFFs trade duplication for autonomy; the trade needs separate teams, conflicting client needs and the capacity to operate them.
  • A query language is a serious alternative when clients differ mainly in which fields they want.
  • Check whether the shared API's backlog is structural or organisational before adopting either pattern.
  • Enforce one consumer per BFF, or it quietly becomes a shared API again and loses its disposability.
  • A rule appearing in two BFFs is evidence of a gap in the services layer, not a case for duplication.
Duplication, Ownership, and the Alternatives · Architecture Atlas