BFF: A design pattern helping teams gain ownership

Part 1: A Leverage for Smoothing Organizations’ Dependencies

Raphaël Tahar
Decathlon Digital
6 min readOct 18, 2023

--

DALL-E interpretation of “Ownership Allegory in a Klimt style”

📖 Series table of content

This series explores the Backend For Frontend design pattern in 4 different dimensions captured in 4 posts.

  1. 👉 Part 1: A Design Pattern Helping Teams Gain Ownership
  2. Part 2: What technical benefits?
  3. Part 3: How to scale and avoid pitfalls?
  4. Part 4: Alternatives & decision tree

🥁 Let’s frame the pattern without further ado and peruse what makes Backends For Frontend worthwhile.

Nominal use case 🚀

Consider the following scenario:

  • 3 Teams: E-commerce, Conversion, and Recommendation
  • Team E-commerce’s product is a frontend (browser)
  • Teams Conversion and Recommendation’s products are APIs (one microservice each)
  • The E-commerce web app directly consumes the Conversion and Recommendation teams' APIs
  • All teams have separate roadmaps and sprints
Use case without BFF

In the use case illustrated above, the E-commerce frontend application directly consumes the Conversion and Recommendation teams’ APIs, which brings a pretty annoying drawback: the dependency between these teams becomes quite strong. A change within the APIs will provoke a mirrored change in the frontend application.

To push this idea further, suppose the Conversion Team’s API is consumed directly by another source, not just the E-commerce Team but a team building a back-office product for internal usage. Any change to the API becomes much more demanding than with a unique consumer, it is now two teams (E-commerce and Back-Office) that will be impacted in case of breaking changes.

It is easy to picture the mess of ever-increasing intricacies and complexity as you add new consumers to the mix.

Strong dependencies between teams

Full retro compatibility will not be an option anymore. The Conversion Team will have to deal with the ever-growing cognitive overhead of designing their APIs without breaking the initial format they were delivering in the first versions.

Any new feature will have to be designed by walking through the valley of past requirements and project specificities that, most of the time, became obsolete 4 to 6 months after being released.

Some might say that API versioning is here to fix this particular issue.

Well, yes and no. That’s sadly not a silver bullet because a team can maintain existing routes but will have a hard time mapping the Database schema evolutions with those previous usages and Domain definitions.

The Conversion Team could also notify a deprecation warning and give its consumers (E-commerce and Back-office Teams) a quarter to migrate to the new API. So, previous versions wouldn’t cause any overhead. (or at least for a minimal time)

Indeed, this could do the trick if your organization mainly builds web applications (running in a browser). However, it would often require deep changes within the frontend application. Depending on your project’s architecture, adaptations would have to be performed either on a core layer (i.e. store data normalization & denormalization within thunks, sagas, or observables) or at a deeper level within the components themselves (i.e. store abstractions that automate normalization through caches aka React/Svelte/Vue-Query) which would involve much bigger refactorings.

Note: This won’t fix the roadmaps collision issue since the frontend team must make room in its quarter backlog to perform the migration.

That being said, imagine that another consumer, the Mobile E-commerce Team, is building a mobile application against that API.

The mobile app use case 📱

How could this team perform the migration?

That’s simple, they couldn’t!
Even if they’d make room in their roadmap. For the simple reason that they don’t know which version of their application is in circulation (which depends on end-users’ manual action to upgrade apps installed on their phones).

The mobile app’s particular use case

This would leave the Conversion Team with no choice but to handle an additional complexity induced by another team’s architecture decision. Retro compatibility forever.

We can do better regarding team autonomy (limiting the negative global impacts of local decisions, which is crucial to building empowered teams).

This is where the Backend For Frontend pattern comes into play!

Architecture tradeoffs ⚖️

Before detailing what BFFs are, let’s quickly take a few moments to shape a shared mental model over architecture tradeoffs. This will help better appreciate the tradeoffs at stake.

Ultimately, architecture is a balance between:

  • The Cost of Building
    Costs of implementation and cost of acquisition (build vs. buy).
  • The Cost of Changing
    More or less the Lead Time DORA metric, aka the efforts per feature.
  • The Cost of Owning
    Costs unrelated to the system and required for its maintenance (security patches, third-party components upgrades, observability & monitoring…).

The Backend For Frontend Design Pattern 🧩

The Backend For Frontend pattern aims at creating a new layer (understand service) that lives within the backend dimension but is owned and maintained by a frontend team.

From a strongly coupled architecture (left) to a loosely coupled BFF architecture (right)

The goal of this layer is to be a middleware handling orchestration and aggregation. When hit by a frontend query, the BFF will call the APIs to retrieve pertinent data to build up tailor-made responses.

If we return to our initial team autonomy use case, this new abstraction layer allows a better separation of concerns and more flexibility by decoupling the system’s components.

Especially regarding the cost of change since it reduces the need for teams to synchronize. They can now focus on what is relevant and vital for their roadmap.

Indeed, suppose the Conversion Team decides to tweak their API and brings changes in the format they provide. In that case, the BFF layer will centralize all mirrored changes without having to tweak or modify the frontend application (fewer chances of business logic leaking into client apps and mobile applications, BFFs relieve API owners’ from the cognitive load and burden of handling strict retro-compatibility).

Backend For Frontend can be used to preserve team autonomy and drastically lower the Cost of Changing in exchange for a slightly higher Cost of Owning and Building (since this is a new layer to build and maintain).

That being said, since the matter at hand is all about interfaces and APIs, features involving those layers have a cross-team cost of owning and building. BFF highly reduces global costs by transferring an expensive cross-team-level cost of change into a team-level slightly higher cost of building and owning.

To better grasp the average gains, here’s the tradeoff equation:

BFF equation = Cost of Changing(- - -) + Cost of Owning(+) + Cost of Building(+)

Conclusion

The BFF pattern provides client applications with custom-fit data through a more decoupled overall architecture.

We’ve been focusing on the organizational aspect of BFFs’ impacts (Conway’s Law) since it is, like most of the time, one of the most extensive cost-killing dimensions.

Discussion and coordination with (too) many stakeholders with sometimes opposed priorities can take much time and effort. BFFs are an excellent way of limiting priority conflicts by providing flexibility through decoupled components.

But BFFs exist in several forms and provide many other benefits; check out our next post exploring this more in-depth: “What technical benefits?”!

Thanks for reading! 🙏🏼
👏🏻👏🏻👏🏻 Give a few claps and “
follow” if you enjoyed this series.

💌 Follow our latest posts on Twitter and LinkedIn and discover our latest stories on Medium 🚀

Acknowledgments
And a big thank you to Jérome Molière, Laurent Thiebault, Alexandre Faria, and Ramzi ACHOURI for their thorough reviews and feedback. Thanks guys!

--

--

Raphaël Tahar
Decathlon Digital

Staff Engineer, Chapter Lead and philosophy enthusiast. Proud dog father 🐶. Opinions are my own.