BFF: Alternatives & decision tree

Part 4: Picking the right solution for your team

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

--

DALL-E interpretation of a “Decision tree 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

There are a few alternatives to BFF, and they each fit an early-stage organizational scale.

Adding a new layer, such as a BFF, into an architecture mix might not be suitable for small teams who can’t support the cost of building and owning it.

BFFs can be:

  • crucial in larger organizations using many backend services
  • helpful within architecture with few backend services
  • a burden for small teams.

For a hammer, every problem is a nail.

Let’s not become software engineering hammers and adapt our tools depending on contexts by perusing the BFF design pattern alternatives before analyzing the context landscape where each option shines the most — starting with the OTA.

Over The Air (OTA) 🌬

For small mobile teams building non-native mobile apps, there is an alternative to building BFFs through automatic over-the-air deployments (a.k.a. OTA), also called Code Push.

This allows non-native applications to push code updates to end-users’ phones without requiring action. This is still a grey area and answers to strict conditions; please ensure that the stores allow your use cases.

As of today (September 2023), CodePush allows you to follow these rules in full compliance so long as the updated version of the code you push does not significantly deviate your product from its original App-Store approved intent.

Google Play’s “Device and Network Abuse” policy states that updating source code from any means other than Google Play’s store mechanism is prohibited, except for updating JavaScript bundles.

This restriction does not apply to code that runs in a virtual machine and has limited access to Android APIs (such as JavaScript in a webview or browser).
- Google Play, Device and Network Abuse Policy

For the simple reason that contrary to native source code, JavaScript bundles can’t directly access every OS feature, thus drastically reducing the surface of possible security issues. Indeed, interpreted code is run in web views and sandboxes.

Interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and © does not bypass signing, sandbox, or other security features of the OS.
- App Store, Apple Developer Program License Agreement

This pattern can be a game changer for your team as it brings flexibility and doesn’t add any new layer that must be maintained alongside the mobile application itself (unlike BFFs) but is only available for stacks built upon non-native technologies (such as React-Native or Flutter).

For instance, Microsoft provides an open-source React-Native Code Push plugin that is a Higher Order Component (HOC) wrapping your root component, providing over-the-air deployment capabilities to your application.

// CodePush class component example
import codePush from "react-native-code-push";

@codePush
class MyApp extends Component {
}

// CodePush functional component example
import codePush from "react-native-code-push";

const MyApp: () => React$Node = () => {
}

export default codePush(MyApp);

By default, Code Push will check for any new updates at each application (re)start and silently download any new version if a version delta is spotted. Of course, this behavior can be fine-tuned through several options provided by the tool (polling, trigger on user action…).

Check out the official plugin’s document if you want to know more.

HATEOAS 🔗

Teams within smaller organizations who develop native applications and can’t afford to build a BFF can reach flexibility through HATEOAS (specifically, its HAL implementation).

Hypermedia as the engine of application state (HATEOAS) is a constraint of the REST application architecture that distinguishes it from other network application architectures.

With HATEOAS, a client interacts with a network application whose application servers provide information dynamically through hypermedia. A REST client needs little prior knowledge about interacting with an application or server beyond a generic understanding of hypermedia.

- Wikipedia

HATEOAS promises to deliver self-describing APIs upon which client applications can base their inner business logic to implement dynamic experiences.

The dynamic nature of self-describing APIs inverses the dependency link between frontend and backend applications. It allows backend teams to dynamically provide frontend applications with ways of querying APIs and adapting them depending on available resources. In other words, frontend applications must be designed to self-adapt based on APIs’ dynamic self-descriptions.

Let’s follow the Wikipedia example to understand the standard better.

The following client call:

GET /accounts/12345 HTTP/1.1
Host: bank.example.com

Would get this response:

HTTP/1.1 200 OK
{
"account": {
"account_number": 12345,
"balance": {
"currency": "usd",
"value": -25.00
},
"links": {
"deposits": "/accounts/12345/deposits"
}
}
}

A unique entry point (bank.example.com/accounts/12345) leads to a resource and provides every possible subsequent query in the response alongside the requested data.

Since the API has the hand, it’ll be its job to adapt the list of actions depending on the data state.

HTTP/1.1 200 OK
{
"account": {
"account_number": 12345,
"balance": {
"currency": "usd",
"value": -25.00
},
"links": {
"deposits": "/accounts/12345/deposits"
}
}
}

For example, an account with a negative balance won’t provide other actions than a “deposit”.

If you want to get your hands dirty, a few JavaScript clients implement the HATEOAS standard (check out these two libraries: Traverson and Ketting).

Of course, HATEOAS offers flexibility to an extent, but designing a client application able to be fully dynamic based on an API self-description can also be challenging. This solution impacts both frontend and backend applications as it is an entire norm. So, before adopting it within your organization, discussion between teams is an obligatory pathway.

It is advised to choose it only if flexibility is not an option and your scale level prevents you from picking other solutions.

Server-Driven UI

There is another way to implement such a responsibility inversion called Server-Driven UI (SDUI).

This pattern requires a much more mature tech organization as it needs a definitive Design System to be settled. It also requires a strong will to standardize and mutualize the user experience by coupling backend and frontend applications.

To better understand the concept, let’s describe the standard flow of a mobile/server architecture.

The mobile application installed on end-users’ phones, performs queries to a backend service and hydrates its internal UI components with the responded data. In other words, the mobile app embeds UI components that constitute the backbone or skeleton of the app, and the retrieved data is the flesh and organs that give it its substance.

SDUI proposes to shrink UI components to their bare minimal version: a Design System, and lets servers the responsibility to provide both the data and a definition of how the mobile app should combine and render the atomic pieces of the Design System.

// Example of payload containing data & UI definition
// This is what a server implementing SDUI could answer
{
ui: {
// Referencing UI components declared in the Design System
component: "TABLE_COMPONENT",
childComponent: "ROW_COMPONENT",
style: "width: '100%', height: 200rem",
spacing: "DESIGN_SYSTEM_SPACING_VARIANT_SMALL",
divider: "DIVIDER_COMPONENT"
},
data: [
{row1},
{row2},
{row3}
]
}

No particular tools are implementing SDUI, this design pattern may have as many implementations as mobile applications available in stores.

The definition format is at the heart of this concept. It can go from a simple app’s theme styling specification (as a string) to a fully configurable UI where each component, their lifecycles, and side effect actions can be specified and provided on the fly by the servers (as an extensive multi-layered JSON configuration object).

The downside of Server-Driven UI is the heavy network usage it creates. Indeed, traditional mobile applications are installed once and include every UI-specific detail. Here, the SDUI pattern will load additional styling & UI layout definitions on every page (re)render which causes a non-negligible network overhead and additional CPU computation (the phone will have to handle the extra UI layout description payload and the attached data to hydrate it).

Recommendations

Before concluding this series, let’s take a few moments to draw a big picture of the most adequate solutions per organizational scale level, technology, and team maturity.

Matrix: Recommended solution per team size & technology types

❌ Not available
⚠️ Available but not recommended
✅ Available and recommended

Native

If you’re responsible for a mobile native application, over-the-air deployment won’t be an option for you (for security reasons). However, a BFF, SDUI, or a HATEOAS implementation is mandatory to bring flexibility and not hinder your backend APIs.

The size of your organization won’t impact the choices you might make in this area. Still, prior discussions with teams and engineers might be required if you decide to use SDUI or HATEOAS, as it is not broadly known and will impact API consumers and producers. Opt for these options if your frontend team can’t afford to build and own a BFF, and your backend team(s) can provide custom-made services with a strong coupling with frontend clients.

Note: Mixing BFFs with SDUI or HATEOAS is also possible. It can enhance the ownership of a frontend team and provide greater deployment flexibility at the cost of higher complexity. As always, pick what makes the more sense depending on your context.

Non-native

OTA is highly recommended for mobile non-native applications as it avoids the burden of maintaining an additional BFF layer. It also has the benefit of bypassing the cumbersome store validation process, which is an invaluable advantage if you’re searching for shortened Lead Time and Mean Time To Repair (features reaching production, bug (hot)fixes, and dependencies breaking changes).

That being said, BFF can still be useful (at any organizational scale) to optimize network usage through unified endpoint calls and data aggregation and formatting, but not as an organizational flexibility enhancer.

SDUI or HATEOAS are not recommended here as they would heavily impact the backend architecture without any gains compared to OTA, except for larger organizations due to their vast number of teams and domains. They could benefit from replacing BFFs with SDUI or HATEOAS implementations to limit business logic duplication and product overlaps.

Web

Regarding web applications, over-the-air deployments are the de facto standard since browsers pull new sources on the fly at each app rendering (modulo caching mechanisms client side, of course), but don’t require any configuration or specific tooling.

BFFs are recommended for web app products within almost any organization, except for small teams where additional maintenance might be too heavy for a minimal gain regarding team autonomy (since there’s only a limited number of teams and ultimately a limited number of services to interface with).

Lastly, SDUI or HATEOAS can help limit code duplication within larger organizations at the cost of spending time getting a consensus among teams that will consume or build those APIs and services. But again, walk down this path only if BFFs are not an option.

Note that SDUI and HATEOAS won’t provide the benefits provided by BFFs on network costs optimization, tailor fit response payloads, blast radius limitation etc.

Conclusion

As a last word, BFFs are not silver bullets and have alternatives depending on the technologies used and the maturity and size of the organization within which it evolves.

HATEOAS and Over-The-Air deployments are good options to bring flexibility to your global architecture through loosely coupled products. SDUI will, on the contrary, strongly couple your architecture components in exchange for seamless and quick deployments. That being said, BFFs have many other benefits not covered by these three solutions, at the price of a higher cost of building and owning. So, as always pick the right solution depending on your context and vision.

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!
Special thanks to
Gérard Paligot for bringing the HATEOAS HAL implementation to my attention.

--

--

Raphaël Tahar
Decathlon Digital

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